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
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,7 @@ function(overrides)
l1_events_scraper_config: {
chain_id: chainId,
finality: 10,
l1_block_time_seconds: 12.0,
l1_block_time_seconds: 12,
polling_interval_seconds: 30.0,
set_provider_historic_height_to_l2_genesis: false,
startup_rewind_time_seconds: 21600.0,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"l1_events_scraper_config.finality": 10,
"l1_events_scraper_config.l1_block_time_seconds": 12.0,
"l1_events_scraper_config.l1_block_time_seconds": 12,
"l1_events_scraper_config.polling_interval_seconds": 30,
"l1_events_scraper_config.set_provider_historic_height_to_l2_genesis": false,
"l1_events_scraper_config.startup_rewind_time_seconds": 21600
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"l1_events_scraper_config.finality": 10,
"l1_events_scraper_config.l1_block_time_seconds": 12.0,
"l1_events_scraper_config.l1_block_time_seconds": 12,
"l1_events_scraper_config.polling_interval_seconds": 30,
"l1_events_scraper_config.set_provider_historic_height_to_l2_genesis": false,
"l1_events_scraper_config.startup_rewind_time_seconds": 21600
Expand Down
3 changes: 2 additions & 1 deletion crates/apollo_deployments/src/deployment_definitions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ mod deployment_definitions_test;
pub(crate) const CONFIG_BASE_DIR: &str = "crates/apollo_deployments/resources/";
pub(crate) const RETRIES_FOR_L1_SERVICES: usize = 0;

const BASE_APP_CONFIGS_DIR_PATH: &str = "crates/apollo_deployments/resources/app_configs";
pub(crate) const BASE_APP_CONFIGS_DIR_PATH: &str =
"crates/apollo_deployments/resources/app_configs";

#[derive(
Hash, Clone, Debug, Display, Serialize, PartialEq, Eq, PartialOrd, Ord, EnumIter, AsRefStr,
Expand Down
17 changes: 16 additions & 1 deletion crates/apollo_deployments/src/deployment_definitions_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,28 @@ use crate::deployment_definitions::ComponentConfigInService;
use crate::deployments::consolidated::ConsolidatedNodeServiceName;
use crate::deployments::distributed::DistributedNodeServiceName;
use crate::deployments::hybrid::HybridNodeServiceName;
use crate::jsonnet::{assert_build_deserializes, assert_infra_matches_rust};
use crate::jsonnet::{
assert_build_deserializes,
assert_infra_matches_rust,
test_applicative_matches_app_configs,
};
use crate::service::NodeType;
use crate::test_utils::SecretsConfigOverride;

const SECRETS_FOR_TESTING_ENV_PATH: &str =
"crates/apollo_deployments/resources/testing_secrets.json";

/// Verifies the applicative config emitted by jsonnet matches the committed `app_configs/*.json`
/// (the deployment's non-overridable value layer), up to overridable keys, secrets, and integers
/// jsonnet can't represent.
#[test]
fn applicative_matches_app_configs() {
env::set_current_dir(resolve_project_relative_path("").unwrap())
.expect("Couldn't set working dir.");

test_applicative_matches_app_configs();
}

/// Verifies the jsonnet hybrid infra config matches the Rust deployment definitions (hybrid.rs).
#[test]
fn hybrid_infra_matches_rust() {
Expand Down
100 changes: 98 additions & 2 deletions crates/apollo_deployments/src/jsonnet.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
use std::collections::{BTreeMap, BTreeSet};
use std::path::PathBuf;

use apollo_node_config::node_config::SequencerNodeConfig;
use apollo_config::dumping::SerializeConfig;
use apollo_config::{FIELD_SEPARATOR, IS_NONE_MARK};
use apollo_node_config::config_utils::{config_to_preset, private_parameters};
use apollo_node_config::node_config::{SequencerNodeConfig, CONFIG_POINTERS};
use jrsonnet_evaluator::trace::PathResolver;
use jrsonnet_evaluator::{FileImportResolver, State};
use serde_json::Value;
use strum::IntoEnumIterator;

use crate::service::{GetComponentConfigs, NodeService, NodeType};
use crate::deployment_definitions::BASE_APP_CONFIGS_DIR_PATH;
use crate::service::{GetComponentConfigs, NodeService, NodeType, KEYS_TO_BE_REPLACED};
use crate::test_utils::is_path_prefix;

const JSONNET_DIR: &str = "crates/apollo_deployments/jsonnet";
const TESTING_OVERRIDES_PATH: &str = "testing/overrides.libsonnet";
Expand Down Expand Up @@ -102,6 +108,96 @@ where
}
}

/// Asserts the applicative config emitted by jsonnet reproduces the committed `app_configs/*.json`
/// for every keys, except keys that are overridable, secret, or under `components.*`.
pub fn test_applicative_matches_app_configs() {
// Applicative side: the single consolidated `node` service carries every component's business
// config; round-trip through the config struct and render it in the app_configs preset format.
let built = eval_build("consolidated", TESTING_OVERRIDES_PATH);
let node = built.get("node").expect("consolidated has a `node` service").clone();
let parsed: SequencerNodeConfig = serde_json::from_value(node).unwrap();
let build_preset = config_to_preset(&serde_json::json!(parsed.dump()));
let build_map = build_preset.as_object().unwrap();

let excluded = non_default_paths();
let is_excluded = |path: &str| {
is_path_prefix("components", path) || excluded.iter().any(|key| is_path_prefix(key, path))
};

let app_config_map = merged_app_configs();

let mut mismatches = Vec::new();
for (key, app_config_value) in &app_config_map {
if is_excluded(key) {
continue;
}
Comment thread
cursor[bot] marked this conversation as resolved.
match build_map.get(key) {
Some(build_value) => {
if build_value != app_config_value {
mismatches.push(format!(
"{key}: applicative={build_value} app_config={app_config_value}"
));
}
}
None => mismatches
.push(format!("{key}: missing in applicative (app_config={app_config_value})")),
}
}

assert!(
mismatches.is_empty(),
"applicative config diverges from app_configs/*.json at {} non-overridable, non-secret \
keys:\n {}",
mismatches.len(),
mismatches.join("\n ")
);
}

/// Merges every base `app_configs/<component>.json` (skipping the derived `replacer_*` files) into
/// a single flat dotted-key map.
fn merged_app_configs() -> BTreeMap<String, Value> {
let mut app_config_map: BTreeMap<String, Value> = BTreeMap::new();
for entry in std::fs::read_dir(BASE_APP_CONFIGS_DIR_PATH).expect("app_configs dir exists") {
let path = entry.expect("readable dir entry").path();
let is_json = path.extension().is_some_and(|extension| extension == "json");
let is_replacer = path.file_name().unwrap().to_string_lossy().starts_with("replacer_");
if !is_json || is_replacer {
continue;
}
let contents = std::fs::read_to_string(&path).expect("app_config file is readable");
let object: serde_json::Map<String, Value> =
serde_json::from_str(&contents).expect("app_config is a JSON object");
app_config_map.extend(object);
}
app_config_map
Comment thread
cursor[bot] marked this conversation as resolved.
}

/// The config paths that are overridable or secrets or passed as pointers.
fn non_default_paths() -> BTreeSet<String> {
// An optional config is marked overridable/secret as `<path>.#is_none`; the override replaces
// the whole option, so exclude the `<path>` subtree (not just the marker).
let is_none_suffix = format!("{FIELD_SEPARATOR}{IS_NONE_MARK}");
let insert_with_option_root = |paths: &mut BTreeSet<String>, key: &str| {
paths.insert(key.to_string());
if let Some(option_root) = key.strip_suffix(&is_none_suffix) {
paths.insert(option_root.to_string());
}
Comment thread
cursor[bot] marked this conversation as resolved.
};

let mut paths = BTreeSet::new();
for key in KEYS_TO_BE_REPLACED.iter() {
insert_with_option_root(&mut paths, key);
}
for ((target_path, _param), pointing_paths) in CONFIG_POINTERS.iter() {
paths.insert(target_path.clone());
paths.extend(pointing_paths.iter().cloned());
}
for key in private_parameters() {
insert_with_option_root(&mut paths, &key);
}
paths
}

/// Clones a `components` map with `url` and `port` removed from each component object — the two
/// fields the Rust config leaves as deploy-time placeholders, so they can't be compared against the
/// jsonnet's baked-in real values.
Expand Down
6 changes: 6 additions & 0 deletions crates/apollo_deployments/src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ use url::Url;

pub(crate) const FIX_BINARY_NAME: &str = "deployment_generator";

/// Returns `true` if `prefix` is a path-prefix of the dotted config key `path`: either the same key
/// or a dot-bounded ancestor of it (so `range_check` does not match the sibling `range_check96`).
pub(crate) fn is_path_prefix(prefix: &str, path: &str) -> bool {
path.strip_prefix(prefix).is_some_and(|rest| rest.is_empty() || rest.starts_with('.'))
}

#[derive(Serialize)]
pub struct SecretsConfigOverride {
#[serde(
Expand Down
Loading