Files
khadhroony-solana-project/crates/ksp-config-lib/src/store.rs

281 lines
12 KiB
Rust

// file: crates/ksp-config-lib/src/store.rs
// version: 2
/// Effective standard Store configuration mapped to `ksp_store_lib::StoreSettings`.
pub struct ResolvedStoreConfig {
effective: crate::ResolvedConfigJson,
file_id: crate::ConfigFileId,
profile_id: String,
selection_source: crate::ConfigProfileSelectionSource,
settings: ksp_store_lib::StoreSettings,
source_path: std::path::PathBuf,
}
impl ResolvedStoreConfig {
/// Returns the detailed environment-resolved Config view.
#[must_use]
pub const fn effective(&self) -> &crate::ResolvedConfigJson {
return &self.effective;
}
/// Returns the logical Config file identifier used by this runtime configuration.
#[must_use]
pub const fn file_id(&self) -> &crate::ConfigFileId {
return &self.file_id;
}
/// Returns the selected standard Store profile identifier.
///
/// For `std.store`, the profile identifier is also the stable named Store target identifier.
#[must_use]
pub fn profile_id(&self) -> &str {
return self.profile_id.as_str();
}
/// Returns the selected named Store target identifier.
#[must_use]
pub fn target_id(&self) -> &str {
return self.profile_id.as_str();
}
/// Returns the source that selected the standard Store profile.
#[must_use]
pub const fn selection_source(&self) -> crate::ConfigProfileSelectionSource {
return self.selection_source;
}
/// Borrows the backend-neutral Store settings without exposing the connection URI.
#[must_use]
pub const fn settings(&self) -> &ksp_store_lib::StoreSettings {
return &self.settings;
}
/// Consumes the resolved Config and returns the Store-owned runtime settings.
#[must_use]
pub fn into_settings(self) -> ksp_store_lib::StoreSettings {
return self.settings;
}
/// Returns the physical source Config document path.
#[must_use]
pub fn source_path(&self) -> &std::path::Path {
return self.source_path.as_path();
}
}
impl std::fmt::Debug for ResolvedStoreConfig {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
return formatter
.debug_struct("ResolvedStoreConfig")
.field("effective", &self.effective)
.field("file_id", &self.file_id)
.field("profile_id", &self.profile_id)
.field("selection_source", &self.selection_source)
.field("settings", &self.settings)
.field("source_path", &self.source_path)
.finish();
}
}
impl crate::ConfigDocumentEngine {
/// Loads `std.store`, resolves one profile/environment and maps it to backend-neutral Store settings.
pub fn load_resolved_store_config(
&self,
requested_profile: std::option::Option<&str>,
environment: &crate::ConfigEnvironment,
) -> ksp_core_lib::Result<ResolvedStoreConfig> {
let file_id = crate::ConfigFileId::new(crate::FILE_ID_STD_STORE);
let file_id = match file_id {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let profile = self.load_resolved_profile(&file_id, requested_profile);
let profile = match profile {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
return resolve_store_profile(&profile, environment);
}
/// Maps an already resolved `cfg.std.store` profile while preserving its selection provenance.
pub fn resolve_store_config_profile(
&self,
profile: &crate::ResolvedConfigProfile,
environment: &crate::ConfigEnvironment,
) -> ksp_core_lib::Result<ResolvedStoreConfig> {
if profile.file_id().as_str() != crate::FILE_ID_STD_STORE {
return std::result::Result::Err(effective_error(profile, "resolved Config profile does not reference the standard Store document"));
}
let descriptor = self.registry().descriptor(profile.file_id());
if let std::result::Result::Err(error) = descriptor {
return std::result::Result::Err(error);
}
return resolve_store_profile(profile, environment);
}
}
#[derive(serde::Deserialize)]
#[serde(deny_unknown_fields)]
struct EffectiveStoreSource {
backend: String,
format_version: u32,
network: String,
postgres: EffectivePostgresSource,
profile_id: String,
}
#[derive(serde::Deserialize)]
#[serde(deny_unknown_fields)]
struct EffectivePostgresSource {
bootstrap: EffectivePostgresBootstrapSource,
connection_uri: String,
pool: EffectivePostgresPoolSource,
shutdown_timeout_ms: u64,
tls: EffectivePostgresTlsSource,
}
#[derive(serde::Deserialize)]
#[serde(deny_unknown_fields)]
struct EffectivePostgresPoolSource {
connect_timeout_ms: u64,
create_timeout_ms: u64,
max_connections: u32,
recycle_timeout_ms: u64,
wait_timeout_ms: u64,
}
#[derive(serde::Deserialize)]
#[serde(deny_unknown_fields)]
struct EffectivePostgresTlsSource {
mode: String,
}
#[derive(serde::Deserialize)]
#[serde(deny_unknown_fields)]
struct EffectivePostgresBootstrapSource {
auto_migrate: bool,
migration_lock_timeout_ms: u64,
migration_timeout_ms: u64,
}
fn resolve_store_profile(profile: &crate::ResolvedConfigProfile, environment: &crate::ConfigEnvironment) -> ksp_core_lib::Result<ResolvedStoreConfig> {
ksp_logging_lib::trace!(target: crate::TRACING_TARGET, profile_id = profile.profile_id(), "mapping standard Store Config profile");
let effective = profile.resolve_effective_environment_detailed(environment);
let effective = match effective {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let provenance = validate_connection_uri_provenance(&effective, profile);
if let std::result::Result::Err(error) = provenance {
return std::result::Result::Err(error);
}
let source = serde_json::from_value::<EffectiveStoreSource>(effective.value().clone());
let source = match source {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => {
return std::result::Result::Err(
effective_error(profile, "effective Store Config cannot be decoded into the runtime adapter contract").with_source(error),
);
},
};
if source.format_version != 1 {
return std::result::Result::Err(effective_error(profile, "effective Store format_version is unsupported"));
}
if source.profile_id != profile.profile_id() {
return std::result::Result::Err(effective_error(profile, "effective Store profile_id does not match the selected profile"));
}
if source.backend != "postgres" {
return std::result::Result::Err(effective_error(profile, "effective Store backend is unsupported").with_context("backend", source.backend));
}
let tls_mode = match source.postgres.tls.mode.as_str() {
"disabled" => ksp_store_lib::PostgresTlsMode::Disabled,
"verify_full" => ksp_store_lib::PostgresTlsMode::VerifyFull,
_ => return std::result::Result::Err(effective_error(profile, "effective Store PostgreSQL TLS mode is unsupported")),
};
let pool = ksp_store_lib::PostgresPoolSettings::new(
source.postgres.pool.max_connections,
std::time::Duration::from_millis(source.postgres.pool.connect_timeout_ms),
std::time::Duration::from_millis(source.postgres.pool.wait_timeout_ms),
std::time::Duration::from_millis(source.postgres.pool.create_timeout_ms),
std::time::Duration::from_millis(source.postgres.pool.recycle_timeout_ms),
);
let bootstrap = ksp_store_lib::PostgresBootstrapSettings::new(
source.postgres.bootstrap.auto_migrate,
std::time::Duration::from_millis(source.postgres.bootstrap.migration_timeout_ms),
std::time::Duration::from_millis(source.postgres.bootstrap.migration_lock_timeout_ms),
);
let network = ksp_store_lib::RawNetworkId::new(source.network);
let network = match network {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::result::Result::Err(effective_error(profile, "effective Store network identifier is invalid")),
};
let postgres = ksp_store_lib::PostgresStoreSettings::new(source.postgres.connection_uri, pool, tls_mode, bootstrap);
let settings = ksp_store_lib::StoreSettings::new(
network,
ksp_store_lib::StoreBackendSettings::Postgres(postgres),
std::time::Duration::from_millis(source.postgres.shutdown_timeout_ms),
);
if let std::result::Result::Err(error) = settings.validate() {
return std::result::Result::Err(store_contract_error(profile, &error));
}
ksp_logging_lib::debug!(
target: crate::TRACING_TARGET,
profile_id = profile.profile_id(),
network = settings.network().as_str(),
backend = settings.backend_kind().code(),
"mapped standard Store Config to Store settings"
);
return std::result::Result::Ok(ResolvedStoreConfig {
effective,
file_id: profile.file_id().clone(),
profile_id: profile.profile_id().to_owned(),
selection_source: profile.selection_source(),
settings,
source_path: profile.path().to_path_buf(),
});
}
fn validate_connection_uri_provenance(effective: &crate::ResolvedConfigJson, profile: &crate::ResolvedConfigProfile) -> ksp_core_lib::Result<()> {
let provenance = match effective.provenance_at("/postgres/connection_uri") {
std::option::Option::Some(value) => value,
std::option::Option::None => return std::result::Result::Err(effective_error(profile, "Store PostgreSQL connection URI provenance is unavailable")),
};
let mut has_secret_environment = false;
for item in provenance {
let variable_name = match item.variable_name() {
std::option::Option::Some(value) => value,
std::option::Option::None => continue,
};
let sensitivity = crate::ConfigSensitivity::from_variable_name(variable_name);
let sensitivity = match sensitivity {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
if !sensitivity.is_secret() {
return std::result::Result::Err(effective_error(profile, "Store PostgreSQL connection URI may reference only secret environment variables"));
}
has_secret_environment = true;
}
if !has_secret_environment {
return std::result::Result::Err(effective_error(profile, "Store PostgreSQL connection URI requires secret environment provenance"));
}
return std::result::Result::Ok(());
}
fn store_contract_error(profile: &crate::ResolvedConfigProfile, error: &ksp_core_lib::Error) -> ksp_core_lib::Error {
return effective_error(profile, "effective Store settings fail the Store runtime contract")
.with_context("store_error_domain", error.code().domain())
.with_context("store_error_code", error.code().code());
}
fn effective_error(profile: &crate::ResolvedConfigProfile, reason: &'static str) -> ksp_core_lib::Error {
return ksp_core_lib::Error::new(crate::ERROR_CODE_EFFECTIVE_CONFIG_INVALID, "effective Config cannot be mapped to the requested runtime contract")
.with_context("file_id", profile.file_id().as_str())
.with_context("profile_id", profile.profile_id())
.with_context("reason", reason);
}
#[cfg(test)]
#[path = "../unit_tests/store.rs"]
mod tests;