Skip to content

Commit 67b6349

Browse files
apollo_config_manager: implement config updates from runner to manager (#9349)
1 parent 7fbd2f7 commit 67b6349

File tree

12 files changed

+193
-78
lines changed

12 files changed

+193
-78
lines changed

Cargo.lock

Lines changed: 4 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/apollo_config_manager/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ workspace = true
1414
[dependencies]
1515
apollo_config_manager_config.workspace = true
1616
apollo_config_manager_types.workspace = true
17+
apollo_consensus_config.workspace = true
1718
apollo_infra.workspace = true
1819
apollo_metrics.workspace = true
1920
apollo_node_config.workspace = true

crates/apollo_config_manager/src/config_manager.rs

Lines changed: 28 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,41 @@
1-
use std::sync::Arc;
2-
31
use apollo_config_manager_config::config::ConfigManagerConfig;
42
use apollo_config_manager_types::communication::{ConfigManagerRequest, ConfigManagerResponse};
53
use apollo_config_manager_types::config_manager_types::ConfigManagerResult;
4+
use apollo_consensus_config::config::ConsensusDynamicConfig;
65
use apollo_infra::component_definitions::{ComponentRequestHandler, ComponentStarter};
76
use apollo_infra::component_server::{ConcurrentLocalComponentServer, RemoteComponentServer};
87
use apollo_node_config::node_config::NodeDynamicConfig;
98
use async_trait::async_trait;
10-
use serde_json::Value;
119
use tracing::{info, instrument};
1210

13-
/// Internal state management for the ConfigManager.
14-
#[derive(Clone)]
15-
pub struct ConfigManagerState {
16-
pub node_dynamic_config: Arc<NodeDynamicConfig>,
17-
}
11+
#[cfg(test)]
12+
#[path = "config_manager_tests.rs"]
13+
pub mod config_manager_tests;
1814

19-
impl Default for ConfigManagerState {
20-
fn default() -> Self {
21-
Self { node_dynamic_config: Arc::new(NodeDynamicConfig::default()) }
22-
}
23-
}
24-
25-
// TODO(Nadin): remove dead_code once we have actual config manager logic
26-
#[allow(dead_code)]
2715
#[derive(Clone)]
2816
pub struct ConfigManager {
29-
config: ConfigManagerConfig,
30-
state: ConfigManagerState,
17+
_config: ConfigManagerConfig,
18+
latest_node_dynamic_config: NodeDynamicConfig,
3119
}
3220

3321
impl ConfigManager {
34-
pub fn new(config: ConfigManagerConfig) -> Self {
35-
let state = ConfigManagerState::default();
36-
info!("ConfigManager initialized with default configuration");
37-
Self { config, state }
22+
pub fn new(config: ConfigManagerConfig, node_dynamic_config: NodeDynamicConfig) -> Self {
23+
Self { _config: config, latest_node_dynamic_config: node_dynamic_config }
3824
}
3925

40-
pub async fn update_config(&mut self) -> ConfigManagerResult<()> {
41-
// TODO(Nadin): Implement actual config update logic
42-
info!("ConfigManager: updating configuration");
43-
26+
pub(crate) fn set_node_dynamic_config(
27+
&mut self,
28+
node_dynamic_config: NodeDynamicConfig,
29+
) -> ConfigManagerResult<()> {
30+
info!("ConfigManager: updating node dynamic config");
31+
self.latest_node_dynamic_config = node_dynamic_config;
4432
Ok(())
4533
}
4634

47-
pub fn get_current_config(&self) -> Arc<Value> {
48-
let config_json = serde_json::to_value(&*self.state.node_dynamic_config)
49-
.unwrap_or_else(|_| serde_json::json!({}));
50-
Arc::new(config_json)
51-
}
52-
53-
pub fn get_node_dynamic_config(&self) -> Arc<NodeDynamicConfig> {
54-
self.state.node_dynamic_config.clone()
35+
pub(crate) fn get_consensus_dynamic_config(
36+
&self,
37+
) -> ConfigManagerResult<ConsensusDynamicConfig> {
38+
Ok(self.latest_node_dynamic_config.consensus_dynamic_config.as_ref().unwrap().clone())
5539
}
5640
}
5741

@@ -65,17 +49,17 @@ impl ComponentRequestHandler<ConfigManagerRequest, ConfigManagerResponse> for Co
6549
#[instrument(skip(self), ret)]
6650
async fn handle_request(&mut self, request: ConfigManagerRequest) -> ConfigManagerResponse {
6751
match request {
68-
ConfigManagerRequest::ReadConfig => {
69-
info!("ConfigManager: handling ReadConfig request");
70-
let config_data = self.get_current_config();
71-
let result = Ok((*config_data).clone());
72-
ConfigManagerResponse::ReadConfig(result)
52+
// TODO(Nadin/Tsabary): consider using a macro to generate the responses for each type
53+
// of request.
54+
ConfigManagerRequest::GetConsensusDynamicConfig => {
55+
ConfigManagerResponse::GetConsensusDynamicConfig(
56+
self.get_consensus_dynamic_config(),
57+
)
7358
}
74-
ConfigManagerRequest::GetNodeDynamicConfig => {
75-
info!("ConfigManager: handling GetNodeDynamicConfig request");
76-
let node_config = self.get_node_dynamic_config();
77-
let result = Ok((*node_config).clone());
78-
ConfigManagerResponse::GetNodeDynamicConfig(result)
59+
ConfigManagerRequest::SetNodeDynamicConfig(new_config) => {
60+
ConfigManagerResponse::SetNodeDynamicConfig(
61+
self.set_node_dynamic_config(new_config),
62+
)
7963
}
8064
}
8165
}

crates/apollo_config_manager/src/config_manager_runner.rs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -56,17 +56,26 @@ impl ConfigManagerRunner {
5656
.expect("consensus_manager_config must be present");
5757

5858
let node_dynamic_config = NodeDynamicConfig {
59-
consensus_dynamic_config: consensus_manager_config
60-
.consensus_manager_config
61-
.dynamic_config
62-
.clone(),
59+
consensus_dynamic_config: Some(
60+
consensus_manager_config.consensus_manager_config.dynamic_config.clone(),
61+
),
6362
};
6463

6564
info!("Extracted NodeDynamicConfig: {:?}", node_dynamic_config);
6665

67-
// TODO(Nadin): Send the new config to the config manager through the client.
68-
69-
Ok(node_dynamic_config)
66+
// TODO(Nadin/Tsabary): Store the last loaded config, compare for changes and only send the
67+
// changes to the config manager.
68+
match self.config_manager_client.set_node_dynamic_config(node_dynamic_config.clone()).await
69+
{
70+
Ok(()) => {
71+
info!("Successfully updated dynamic config");
72+
Ok(node_dynamic_config)
73+
}
74+
Err(e) => {
75+
error!("Failed to update dynamic config: {:?}", e);
76+
Err(format!("Failed to update dynamic config: {:?}", e).into())
77+
}
78+
}
7079
}
7180
}
7281

crates/apollo_config_manager/src/config_manager_runner_tests.rs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,11 @@ fn update_config_file(temp_file: &NamedTempFile) -> String {
8585
}
8686

8787
#[tokio::test]
88-
async fn test_update_config_with_changed_values() {
89-
let config_manager_client: SharedConfigManagerClient = Arc::new(MockConfigManagerClient::new());
88+
async fn test_config_manager_runner_update_config_with_changed_values() {
89+
// Set a mock config manager client to expect the update dynamic config request.
90+
let mut mock_client = MockConfigManagerClient::new();
91+
mock_client.expect_set_node_dynamic_config().times(1..).return_const(Ok(()));
92+
let config_manager_client: SharedConfigManagerClient = Arc::new(mock_client);
9093

9194
// Create a temporary config file and get the validator id value.
9295
let (temp_file, cli_args, validator_id_value) = create_temp_config_file_and_args();
@@ -107,9 +110,11 @@ async fn test_update_config_with_changed_values() {
107110
let first_dynamic_config =
108111
first_update_config_result.expect("First update_config should succeed");
109112
assert_eq!(
110-
first_dynamic_config.consensus_dynamic_config.validator_id, expected_validator_id,
113+
first_dynamic_config.consensus_dynamic_config.as_ref().unwrap().validator_id,
114+
expected_validator_id,
111115
"First update_config: Validator id mismatch: {} != {}",
112-
first_dynamic_config.consensus_dynamic_config.validator_id, expected_validator_id
116+
first_dynamic_config.consensus_dynamic_config.as_ref().unwrap().validator_id,
117+
expected_validator_id
113118
);
114119

115120
// Edit the config file and then trigger a config update, expecting the new validator id.
@@ -120,8 +125,10 @@ async fn test_update_config_with_changed_values() {
120125
let second_dynamic_config =
121126
second_update_config_result.expect("Second update_config should succeed");
122127
assert_eq!(
123-
second_dynamic_config.consensus_dynamic_config.validator_id, expected_validator_id,
128+
second_dynamic_config.consensus_dynamic_config.as_ref().unwrap().validator_id,
129+
expected_validator_id,
124130
"Second update_config: Validator id mismatch: {} != {}",
125-
second_dynamic_config.consensus_dynamic_config.validator_id, expected_validator_id
131+
second_dynamic_config.consensus_dynamic_config.as_ref().unwrap().validator_id,
132+
expected_validator_id
126133
);
127134
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
use apollo_config_manager_config::config::ConfigManagerConfig;
2+
use apollo_consensus_config::config::ConsensusDynamicConfig;
3+
use apollo_consensus_config::ValidatorId;
4+
use apollo_node_config::node_config::NodeDynamicConfig;
5+
6+
use crate::config_manager::ConfigManager;
7+
8+
#[tokio::test]
9+
async fn test_config_manager_update_config() {
10+
// Set a config manager.
11+
let config = ConfigManagerConfig::default();
12+
13+
let consensus_dynamic_config = ConsensusDynamicConfig::default();
14+
let node_dynamic_config =
15+
NodeDynamicConfig { consensus_dynamic_config: Some(consensus_dynamic_config) };
16+
let mut config_manager = ConfigManager::new(config, node_dynamic_config.clone());
17+
18+
// Get the consensus dynamic config and assert it is the expected one.
19+
let consensus_dynamic_config = config_manager
20+
.get_consensus_dynamic_config()
21+
.expect("Failed to get consensus dynamic config");
22+
assert_eq!(
23+
&consensus_dynamic_config,
24+
node_dynamic_config.consensus_dynamic_config.as_ref().unwrap(),
25+
"Consensus dynamic config mismatch: {consensus_dynamic_config:#?} != {:#?}",
26+
node_dynamic_config.consensus_dynamic_config
27+
);
28+
29+
// Set a new dynamic config by creating a new consensus dynamic config. For simplicity, we
30+
// create an arbitrary one and assert it's not the default one.
31+
let new_consensus_dynamic_config =
32+
ConsensusDynamicConfig { validator_id: ValidatorId::from(1_u8) };
33+
assert_ne!(
34+
consensus_dynamic_config, new_consensus_dynamic_config,
35+
"Consensus dynamic config should be different: {consensus_dynamic_config:#?} != {:#?}",
36+
new_consensus_dynamic_config
37+
);
38+
config_manager
39+
.set_node_dynamic_config(NodeDynamicConfig {
40+
consensus_dynamic_config: Some(new_consensus_dynamic_config.clone()),
41+
})
42+
.expect("Failed to set node dynamic config");
43+
44+
// Get the post-change consensus dynamic config and assert it is the expected one.
45+
let consensus_dynamic_config = config_manager
46+
.get_consensus_dynamic_config()
47+
.expect("Failed to get consensus dynamic config");
48+
assert_eq!(
49+
consensus_dynamic_config, new_consensus_dynamic_config,
50+
"Consensus dynamic config mismatch: {consensus_dynamic_config:#?} != {:#?}",
51+
new_consensus_dynamic_config
52+
);
53+
}

crates/apollo_config_manager_types/Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,17 @@ repository.workspace = true
99
workspace = true
1010

1111
[dependencies]
12+
apollo_consensus_config.workspace = true
1213
apollo_infra.workspace = true
1314
apollo_metrics.workspace = true
1415
apollo_node_config.workspace = true
16+
apollo_proc_macros.workspace = true
1517
async-trait.workspace = true
1618
mockall = { workspace = true, optional = true }
1719
serde = { workspace = true, features = ["derive"] }
18-
serde_json.workspace = true
1920
strum = { workspace = true, features = ["derive"] }
2021
strum_macros.workspace = true
22+
thiserror.workspace = true
2123

2224
[dev-dependencies]
2325
mockall.workspace = true

crates/apollo_config_manager_types/src/communication.rs

Lines changed: 55 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
use std::sync::Arc;
22

3-
use apollo_infra::component_client::{LocalComponentClient, RemoteComponentClient};
3+
use apollo_consensus_config::config::ConsensusDynamicConfig;
4+
use apollo_infra::component_client::{ClientError, LocalComponentClient, RemoteComponentClient};
45
use apollo_infra::component_definitions::{ComponentClient, PrioritizedRequest, RequestWrapper};
56
use apollo_infra::{impl_debug_for_infra_requests_and_responses, impl_labeled_request};
67
use apollo_metrics::generate_permutation_labels;
78
use apollo_node_config::node_config::NodeDynamicConfig;
9+
use apollo_proc_macros::handle_all_response_variants;
810
use async_trait::async_trait;
911
use serde::{Deserialize, Serialize};
10-
use serde_json::Value;
1112
use strum::{EnumVariantNames, VariantNames};
1213
use strum_macros::{AsRefStr, EnumDiscriminants, EnumIter, IntoStaticStr};
14+
use thiserror::Error;
1315

1416
use crate::config_manager_types::ConfigManagerResult;
17+
use crate::errors::ConfigManagerError;
1518

1619
pub type LocalConfigManagerClient =
1720
LocalComponentClient<ConfigManagerRequest, ConfigManagerResponse>;
@@ -23,7 +26,16 @@ pub type SharedConfigManagerClient = Arc<dyn ConfigManagerClient>;
2326

2427
#[cfg_attr(any(feature = "testing", test), mockall::automock)]
2528
#[async_trait]
26-
pub trait ConfigManagerClient: Send + Sync {}
29+
pub trait ConfigManagerClient: Send + Sync {
30+
async fn get_consensus_manager_dynamic_config(
31+
&self,
32+
) -> ConfigManagerClientResult<ConsensusDynamicConfig>;
33+
34+
async fn set_node_dynamic_config(
35+
&self,
36+
config: NodeDynamicConfig,
37+
) -> ConfigManagerClientResult<()>;
38+
}
2739

2840
#[derive(Serialize, Deserialize, Clone, AsRefStr, EnumDiscriminants)]
2941
#[strum_discriminants(
@@ -32,8 +44,8 @@ pub trait ConfigManagerClient: Send + Sync {}
3244
strum(serialize_all = "snake_case")
3345
)]
3446
pub enum ConfigManagerRequest {
35-
ReadConfig,
36-
GetNodeDynamicConfig,
47+
GetConsensusDynamicConfig,
48+
SetNodeDynamicConfig(NodeDynamicConfig),
3749
}
3850
impl_debug_for_infra_requests_and_responses!(ConfigManagerRequest);
3951
impl_labeled_request!(ConfigManagerRequest, ConfigManagerRequestLabelValue);
@@ -48,16 +60,48 @@ generate_permutation_labels! {
4860

4961
#[derive(Clone, Serialize, Deserialize, AsRefStr)]
5062
pub enum ConfigManagerResponse {
51-
ReadConfig(ConfigManagerResult<Value>),
52-
GetNodeDynamicConfig(ConfigManagerResult<NodeDynamicConfig>),
63+
GetConsensusDynamicConfig(ConfigManagerResult<ConsensusDynamicConfig>),
64+
SetNodeDynamicConfig(ConfigManagerResult<()>),
5365
}
5466
impl_debug_for_infra_requests_and_responses!(ConfigManagerResponse);
5567

56-
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
57-
pub enum ConfigManagerClientError {}
68+
#[derive(Clone, Debug, Error)]
69+
pub enum ConfigManagerClientError {
70+
#[error(transparent)]
71+
ClientError(#[from] ClientError),
72+
#[error(transparent)]
73+
ConfigManagerError(#[from] ConfigManagerError),
74+
}
5875

5976
#[async_trait]
60-
impl<ComponentClientType> ConfigManagerClient for ComponentClientType where
61-
ComponentClientType: Send + Sync + ComponentClient<ConfigManagerRequest, ConfigManagerResponse>
77+
impl<ComponentClientType> ConfigManagerClient for ComponentClientType
78+
where
79+
ComponentClientType: Send + Sync + ComponentClient<ConfigManagerRequest, ConfigManagerResponse>,
6280
{
81+
async fn get_consensus_manager_dynamic_config(
82+
&self,
83+
) -> ConfigManagerClientResult<ConsensusDynamicConfig> {
84+
let request = ConfigManagerRequest::GetConsensusDynamicConfig;
85+
handle_all_response_variants!(
86+
ConfigManagerResponse,
87+
GetConsensusDynamicConfig,
88+
ConfigManagerClientError,
89+
ConfigManagerError,
90+
Direct
91+
)
92+
}
93+
94+
async fn set_node_dynamic_config(
95+
&self,
96+
config: NodeDynamicConfig,
97+
) -> ConfigManagerClientResult<()> {
98+
let request = ConfigManagerRequest::SetNodeDynamicConfig(config);
99+
handle_all_response_variants!(
100+
ConfigManagerResponse,
101+
SetNodeDynamicConfig,
102+
ConfigManagerClientError,
103+
ConfigManagerError,
104+
Direct
105+
)
106+
}
63107
}

0 commit comments

Comments
 (0)