refactor cfgsync rendering to generic runtime config

This commit is contained in:
andrussal 2026-02-24 03:46:54 +01:00
parent 114b84698d
commit 85a6f9d054
9 changed files with 136 additions and 137 deletions

2
Cargo.lock generated
View File

@ -2894,8 +2894,6 @@ dependencies = [
"cfgsync-runtime",
"kube",
"logos-blockchain-http-api-common",
"logos-blockchain-tracing",
"logos-blockchain-tracing-service",
"reqwest",
"serde",
"serde_yaml",

View File

@ -1,51 +0,0 @@
port: 4400
n_hosts: 4
timeout: 10
global_params_path: "/etc/logos/global_params"
# Optional: serve prebuilt configs from a bundle file.
# bundle_path: cfgsync.bundle.yaml
# ConsensusConfig related parameters
security_param: 10
active_slot_coeff: 0.9
wallet:
accounts: []
# DaConfig related parameters
subnetwork_size: 2
dispersal_factor: 2
num_samples: 1
num_subnets: 2
old_blobs_check_interval: "5.0"
blobs_validity_duration: "60.0"
min_dispersal_peers: 1
min_replication_peers: 1
monitor_failure_time_window: "5.0"
balancer_interval: "5.0"
# Dispersal mempool publish strategy
mempool_publish_strategy: !SampleSubnetworks
sample_threshold: 2
timeout: "2.0"
cooldown: "0.0001"
replication_settings:
seen_message_cache_size: 204800
seen_message_ttl: "900.0"
retry_shares_limit: 5
retry_commitments_limit: 5
# Tracing
tracing_settings:
# Default to stdout so `docker logs` / `kubectl logs` shows node output.
# Switch to `!File` if you want per-node log files inside the container/pod.
logger: Stdout
# Disable OTLP traces to remove DNS errors; metrics stay enabled below.
tracing: None
filter: !EnvFilter
filters:
nomos: debug
cryptarchia: debug
logos_blockchain_chain_leader_service: debug
metrics: None
console: None
level: DEBUG

View File

@ -64,7 +64,7 @@ export CFG_FILE_PATH="/config.yaml" \
CFG_HOST_KIND="${CFG_HOST_KIND:-$role}" \
CFG_HOST_IDENTIFIER="${CFG_HOST_IDENTIFIER:-$host_identifier_default}" \
LOGOS_BLOCKCHAIN_TIME_BACKEND="${LOGOS_BLOCKCHAIN_TIME_BACKEND:-monotonic}" \
LOG_LEVEL="${LOG_LEVEL:-INFO}"``"
LOG_LEVEL="${LOG_LEVEL:-INFO}"
# Ensure recovery directory exists to avoid early crashes in services that
# persist state.

View File

@ -17,8 +17,6 @@ testing-framework-runner-local = { workspace = true }
# Logos / Nomos deps
lb_http_api_common = { workspace = true }
lb_tracing = { workspace = true }
lb_tracing_service = { workspace = true }
# External
anyhow = "1"

View File

@ -1,16 +1,14 @@
mod template;
use std::path::Path;
use anyhow::Result;
pub(crate) use cfgsync_runtime::render::CfgsyncOutputPaths;
use cfgsync_runtime::{
bundle::build_cfgsync_bundle_with_hostnames,
render::{RenderedCfgsync, apply_timeout_floor, ensure_bundle_path, write_rendered_cfgsync},
render::{
CfgsyncConfigOverrides, RenderedCfgsync, ensure_bundle_path,
render_cfgsync_yaml_from_template, write_rendered_cfgsync,
},
};
use lb_tracing::metrics::otlp::OtlpMetricsConfig;
use lb_tracing_service::MetricsLayerSettings;
use reqwest::Url;
use serde_yaml::{Mapping, Value};
use testing_framework_core::cfgsync::CfgsyncEnv;
pub(crate) struct CfgsyncRenderOptions {
@ -21,16 +19,14 @@ pub(crate) struct CfgsyncRenderOptions {
}
pub(crate) fn render_cfgsync_from_template<E: CfgsyncEnv>(
template_path: &Path,
topology: &E::Deployment,
hostnames: &[String],
options: CfgsyncRenderOptions,
) -> Result<RenderedCfgsync> {
let mut cfg = template::load_cfgsync_template(template_path)?;
apply_render_options::<E>(&mut cfg, topology, options);
let cfg = build_cfgsync_server_config();
let overrides = build_overrides::<E>(topology, options);
let config_yaml = render_cfgsync_yaml_from_template(cfg, &overrides)?;
let bundle = build_cfgsync_bundle_with_hostnames::<E>(topology, hostnames)?;
let config_yaml = serde_yaml::to_string(&cfg)?;
let bundle_yaml = serde_yaml::to_string(&bundle)?;
Ok(RenderedCfgsync {
@ -39,8 +35,22 @@ pub(crate) fn render_cfgsync_from_template<E: CfgsyncEnv>(
})
}
fn build_cfgsync_server_config() -> Value {
let mut root = Mapping::new();
root.insert(
Value::String("port".to_string()),
Value::Number(4400_u64.into()),
);
root.insert(
Value::String("bundle_path".to_string()),
Value::String("cfgsync.bundle.yaml".to_string()),
);
Value::Mapping(root)
}
pub(crate) fn render_and_write_cfgsync_from_template<E: CfgsyncEnv>(
template_path: &Path,
topology: &E::Deployment,
hostnames: &[String],
mut options: CfgsyncRenderOptions,
@ -48,16 +58,16 @@ pub(crate) fn render_and_write_cfgsync_from_template<E: CfgsyncEnv>(
) -> Result<RenderedCfgsync> {
ensure_bundle_path(&mut options.bundle_path, output.bundle_path);
let rendered = render_cfgsync_from_template::<E>(template_path, topology, hostnames, options)?;
let rendered = render_cfgsync_from_template::<E>(topology, hostnames, options)?;
write_rendered_cfgsync(&rendered, output)?;
Ok(rendered)
}
fn apply_render_options<E: CfgsyncEnv>(
cfg: &mut template::CfgSyncConfig,
fn build_overrides<E: CfgsyncEnv>(
topology: &E::Deployment,
options: CfgsyncRenderOptions,
) {
) -> CfgsyncConfigOverrides {
let CfgsyncRenderOptions {
port,
bundle_path,
@ -65,21 +75,11 @@ fn apply_render_options<E: CfgsyncEnv>(
metrics_otlp_ingest_url,
} = options;
if let Some(port) = port {
cfg.port = port;
}
cfg.n_hosts = E::nodes(topology).len();
cfg.bundle_path = bundle_path;
apply_metrics_endpoint(cfg, metrics_otlp_ingest_url);
apply_timeout_floor(&mut cfg.timeout, min_timeout_secs);
}
fn apply_metrics_endpoint(cfg: &mut template::CfgSyncConfig, endpoint: Option<Url>) {
if let Some(endpoint) = endpoint {
cfg.tracing_settings.metrics = MetricsLayerSettings::Otlp(OtlpMetricsConfig {
endpoint,
host_identifier: "node".into(),
});
CfgsyncConfigOverrides {
port,
n_hosts: Some(E::nodes(topology).len()),
timeout_floor_secs: min_timeout_secs,
bundle_path,
metrics_otlp_ingest_url: metrics_otlp_ingest_url.map(|url| url.to_string()),
}
}

View File

@ -1,26 +0,0 @@
use std::{fs::File, path::Path};
use anyhow::{Context as _, Result};
use lb_tracing_service::TracingSettings;
use serde::{Deserialize, Serialize};
use tracing::debug;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CfgSyncConfig {
pub port: u16,
#[serde(default)]
pub n_hosts: usize,
pub timeout: u64,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub bundle_path: Option<String>,
#[serde(default)]
pub tracing_settings: TracingSettings,
}
pub fn load_cfgsync_template(path: &Path) -> Result<CfgSyncConfig> {
debug!(path = %path.display(), "loading cfgsync template");
let file = File::open(path)
.with_context(|| format!("opening cfgsync template at {}", path.display()))?;
serde_yaml::from_reader(file).context("parsing cfgsync template")
}

View File

@ -120,8 +120,8 @@ impl ComposeDeployEnv for LbcExtEnv {
let bundle_path = cfgsync_bundle_path(path);
let hostnames = topology_hostnames(topology);
let options = cfgsync_render_options(port, metrics_otlp_ingest_url);
render_and_write_cfgsync_from_template::<lb_framework::LbcEnv>(
path,
topology,
&hostnames,
options,

View File

@ -183,7 +183,7 @@ pub fn prepare_assets(
let tempdir = create_assets_tempdir()?;
let (cfgsync_file, cfgsync_yaml, bundle_yaml) =
render_and_write_cfgsync(&root, topology, metrics_otlp_ingest_url, &tempdir)?;
render_and_write_cfgsync(topology, metrics_otlp_ingest_url, &tempdir)?;
let scripts = validate_scripts(&root)?;
let chart_path = helm_chart_path()?;
let values_file = render_and_write_values(topology, &tempdir, &cfgsync_yaml, &bundle_yaml)?;
@ -346,7 +346,6 @@ fn create_assets_tempdir() -> Result<TempDir, AssetsError> {
}
fn render_and_write_cfgsync(
root: &Path,
topology: &DeploymentPlan,
metrics_otlp_ingest_url: Option<&Url>,
tempdir: &TempDir,
@ -354,12 +353,12 @@ fn render_and_write_cfgsync(
let cfgsync_file = tempdir.path().join("cfgsync.yaml");
let bundle_file = tempdir.path().join("cfgsync.bundle.yaml");
let (cfgsync_yaml, bundle_yaml) = render_cfgsync_config(
root,
topology,
metrics_otlp_ingest_url,
&cfgsync_file,
&bundle_file,
)?;
Ok((cfgsync_file, cfgsync_yaml, bundle_yaml))
}
@ -378,17 +377,13 @@ fn testnet_image() -> String {
}
fn render_cfgsync_config(
root: &Path,
topology: &DeploymentPlan,
metrics_otlp_ingest_url: Option<&Url>,
cfgsync_file: &Path,
bundle_file: &Path,
) -> Result<(String, String), AssetsError> {
let cfgsync_template_path = cfgsync_template_path(root);
debug!(path = %cfgsync_template_path.display(), "loading cfgsync template");
let hostnames = k8s_node_hostnames(topology);
let rendered = render_and_write_cfgsync_from_template::<lb_framework::LbcEnv>(
&cfgsync_template_path,
topology,
&hostnames,
CfgsyncRenderOptions {
@ -407,10 +402,6 @@ fn render_cfgsync_config(
Ok((rendered.config_yaml, rendered.bundle_yaml))
}
fn cfgsync_template_path(root: &Path) -> PathBuf {
stack_assets_root(root).join("cfgsync.yaml")
}
fn k8s_node_hostnames(topology: &DeploymentPlan) -> Vec<String> {
topology
.nodes()
@ -517,15 +508,6 @@ pub fn workspace_root() -> AnyhowResult<PathBuf> {
))
}
fn stack_assets_root(root: &Path) -> PathBuf {
if let Some(override_dir) = assets_override_dir(root)
&& override_dir.exists()
{
return override_dir;
}
root.join(DEFAULT_ASSETS_STACK_DIR)
}
fn stack_scripts_root(root: &Path) -> PathBuf {
if let Some(scripts) = override_scripts_dir(root)
&& scripts.exists()

View File

@ -1,6 +1,7 @@
use std::{fs, path::Path};
use anyhow::Result;
use anyhow::{Context as _, Result};
use serde_yaml::{Mapping, Value};
#[derive(Debug, Clone)]
pub struct RenderedCfgsync {
@ -42,3 +43,100 @@ pub fn write_rendered_cfgsync(
fs::write(output.bundle_path, &rendered.bundle_yaml)?;
Ok(())
}
#[derive(Debug, Clone, Default)]
pub struct CfgsyncConfigOverrides {
pub port: Option<u16>,
pub n_hosts: Option<usize>,
pub timeout_floor_secs: Option<u64>,
pub bundle_path: Option<String>,
pub metrics_otlp_ingest_url: Option<String>,
}
pub fn load_cfgsync_template_yaml(path: &Path) -> Result<Value> {
let file = fs::File::open(path)
.with_context(|| format!("opening cfgsync template at {}", path.display()))?;
serde_yaml::from_reader(file).context("parsing cfgsync template")
}
pub fn render_cfgsync_yaml_from_template(
mut template: Value,
overrides: &CfgsyncConfigOverrides,
) -> Result<String> {
apply_cfgsync_overrides(&mut template, overrides)?;
serde_yaml::to_string(&template).context("serializing rendered cfgsync config")
}
pub fn apply_cfgsync_overrides(
template: &mut Value,
overrides: &CfgsyncConfigOverrides,
) -> Result<()> {
let root = mapping_mut(template)?;
if let Some(port) = overrides.port {
root.insert(
Value::String("port".to_string()),
serde_yaml::to_value(port)?,
);
}
if let Some(n_hosts) = overrides.n_hosts {
root.insert(
Value::String("n_hosts".to_string()),
serde_yaml::to_value(n_hosts)?,
);
}
if let Some(bundle_path) = &overrides.bundle_path {
root.insert(
Value::String("bundle_path".to_string()),
Value::String(bundle_path.clone()),
);
}
if let Some(timeout_floor_secs) = overrides.timeout_floor_secs {
let timeout_key = Value::String("timeout".to_string());
let timeout_value = root
.get(&timeout_key)
.and_then(Value::as_u64)
.unwrap_or(timeout_floor_secs)
.max(timeout_floor_secs);
root.insert(timeout_key, serde_yaml::to_value(timeout_value)?);
}
if let Some(endpoint) = &overrides.metrics_otlp_ingest_url {
let tracing_settings = nested_mapping_mut(root, "tracing_settings");
tracing_settings.insert(
Value::String("metrics".to_string()),
parse_otlp_metrics_layer(endpoint)?,
);
}
Ok(())
}
fn mapping_mut(value: &mut Value) -> Result<&mut Mapping> {
value
.as_mapping_mut()
.context("cfgsync template root must be a YAML map")
}
fn nested_mapping_mut<'a>(mapping: &'a mut Mapping, key: &str) -> &'a mut Mapping {
let key = Value::String(key.to_string());
let entry = mapping
.entry(key)
.or_insert_with(|| Value::Mapping(Mapping::new()));
if !entry.is_mapping() {
*entry = Value::Mapping(Mapping::new());
}
entry
.as_mapping_mut()
.expect("mapping entry should always be a mapping")
}
fn parse_otlp_metrics_layer(endpoint: &str) -> Result<Value> {
let yaml = format!("!Otlp\nendpoint: \"{endpoint}\"\nhost_identifier: \"node\"\n");
serde_yaml::from_str(&yaml).context("building metrics OTLP layer")
}