Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 19 additions & 4 deletions crates/apollo_integration_tests/src/executable_setup.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use tempfile::{tempdir, TempDir};
use tokio::fs::create_dir_all;

const NODE_CONFIG_CHANGES_FILE_PATH: &str = "node_integration_test_config_changes.json";
const NODE_SECRETS_FILE_PATH: &str = "node_integration_test_secrets.json";

#[derive(Debug, Clone)]
pub struct NodeExecutableId {
Expand Down Expand Up @@ -48,8 +49,10 @@ pub struct ExecutableSetup {
pub node_executable_id: NodeExecutableId,
// Client for checking liveness of the sequencer node.
pub monitoring_client: MonitoringClient,
// Path to the node configuration file.
// Path to the nested native base config file (consumed first by the native loader).
pub node_config_path: PathBuf,
// Path to the (empty) secrets file overlaid onto the base by the native loader.
pub node_secrets_path: PathBuf,
// Config.
pub base_app_config: DeploymentBaseAppConfig,
// Handles for the config files, maintained so the files are not deleted. Since
Expand Down Expand Up @@ -84,17 +87,29 @@ impl ExecutableSetup {
let monitoring_client = MonitoringClient::new(SocketAddr::new(*ip, *port));

let config_path = node_config_dir.join(NODE_CONFIG_CHANGES_FILE_PATH);
base_app_config.dump_config_file(&config_path);
base_app_config.dump_native_config_file(&config_path);

// The native loader requires a secrets file overlaid onto the base. The harness carries no
// secrets, so emit an empty JSON object.
let secrets_path = node_config_dir.join(NODE_SECRETS_FILE_PATH);
std::fs::write(&secrets_path, "{}").expect("Should be able to write secrets file");

Self {
node_executable_id,
monitoring_client,
base_app_config,
node_config_dir_handle,
node_config_path: config_path,
node_secrets_path: secrets_path,
}
}

/// Config files passed to the native loader, base first then secrets, matching the
/// `[base, secret]` arity expected by `load_native`.
pub fn node_config_paths(&self) -> Vec<PathBuf> {
vec![self.node_config_path.clone(), self.node_secrets_path.clone()]
}

pub fn modify_config<F>(&mut self, modify_config_fn: F)
where
F: Fn(&mut SequencerNodeConfig),
Expand All @@ -115,8 +130,8 @@ impl ExecutableSetup {
self.base_app_config.get_config()
}

/// Creates a config file for the sequencer node for an integration test.
/// Re-emits the native base config file for the sequencer node after a config change.
pub fn dump_config_file_changes(&self) {
self.base_app_config.dump_config_file(&self.node_config_path);
self.base_app_config.dump_native_config_file(&self.node_config_path);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ impl NodeSetup {
pub fn run_service(&self, service: NodeService) -> AbortOnDropHandle<()> {
let executable_setup = self.get_executable_by_service(service);
spawn_run_node(
vec![executable_setup.node_config_path.clone()],
executable_setup.node_config_paths(),
executable_setup.node_executable_id.clone().into(),
)
}
Expand All @@ -294,7 +294,7 @@ impl NodeSetup {
(
*service,
spawn_run_node(
vec![executable.node_config_path.clone()],
executable.node_config_paths(),
executable.node_executable_id.clone().into(),
),
)
Expand Down
6 changes: 3 additions & 3 deletions crates/apollo_node/src/test_utils/node_runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,9 @@ async fn spawn_node_child_process(
info!("Getting the node executable.");
let node_executable = get_node_executable_path();

// Interpret the config files with the legacy flat-preset loader (the node's default), kept
// explicit so this stays a clear switch point if/when tests move to "native" loading.
let config_file_args: Vec<String> = ["--config_format".to_string(), "preset".to_string()]
// Interpret the config files with the native loader: the paths are a nested base config
// followed by a flat secrets file, matching `load_native`'s `[base, secret]` arity.
let config_file_args: Vec<String> = ["--config_format".to_string(), "native".to_string()]
.into_iter()
.chain(node_config_paths.into_iter().flat_map(|path| {
let path_str = path.to_str().expect("Invalid path").to_string();
Expand Down
16 changes: 16 additions & 0 deletions crates/apollo_node_config/src/config_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,13 @@ impl DeploymentBaseAppConfig {
preset
}

/// Returns the nested config as JSON, matching the `SequencerNodeConfig` field hierarchy.
/// This is the artifact consumed by the `ConfigFormat::Native` loader (as the base config),
/// in contrast to the flat preset produced by `as_value`.
pub fn as_native_value(&self) -> Value {
serde_json::to_value(&self.config).expect("Should be able to serialize config to value")
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Native dump skips pointer map

Medium Severity

as_native_value serializes only self.config, while as_value merges config_pointers_map via combine_config_map_and_pointers. The native loader does not resolve pointers, so booted nodes can see different values (e.g. shared recorder_url, native_classes_whitelist) than under the previous preset file path, including after modify_config_pointers without matching struct updates.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 0797ffc. Configure here.


// TODO(Tsabary): unify path types throughout.
pub fn dump_config_file(&self, config_path: &Path) {
let value = self.as_value();
Expand All @@ -204,6 +211,15 @@ impl DeploymentBaseAppConfig {
config_path.to_str().expect("Should be able to convert path to string"),
);
}

/// Dumps the nested native base config (see `as_native_value`) to `config_path`.
pub fn dump_native_config_file(&self, config_path: &Path) {
let value = self.as_native_value();
serialize_to_file(
&value,
config_path.to_str().expect("Should be able to convert path to string"),
);
}
}

pub fn load_and_validate_config(
Expand Down
Loading