diff --git a/src/migrate.rs b/src/migrate.rs index fab7901..746a60d 100644 --- a/src/migrate.rs +++ b/src/migrate.rs @@ -8,6 +8,7 @@ use crate::config_json::ConfigMap; use crate::remote::ConfigMigrationInstructions; use crate::schema::OsConfigSchema; use anyhow::Result; +use std::collections::HashMap; pub fn generate_config_json_migration( schema: &OsConfigSchema, @@ -18,31 +19,52 @@ pub fn generate_config_json_migration( let mut new_config = config_json.clone(); - handle_update_directives(schema, migration_config, config_json, &mut new_config); + let (to_update, to_delete) = separate_migration_directives(migration_config); - handle_delete_directives(schema, migration_config, config_json, &mut new_config); + handle_update_directives(schema, to_update, &to_delete, config_json, &mut new_config); + + handle_delete_directives(schema, &to_delete, config_json, &mut new_config); Ok(new_config) } +// Separate the migration_config.overrides directives into update +// or delete directives. If a value is null, it is considered a delete directive. +fn separate_migration_directives( + migration_config: &ConfigMigrationInstructions, +) -> (HashMap, Vec) { + let mut to_update = migration_config.overrides.clone(); + let mut to_delete = Vec::new(); + + for (key, value) in migration_config.overrides.iter() { + if value.is_null() { + to_update.remove(key); + to_delete.push(key.to_string()); + } + } + + (to_update, to_delete) +} + fn handle_update_directives( schema: &OsConfigSchema, - migration_config: &ConfigMigrationInstructions, + to_update: HashMap, + to_delete: &Vec, config_json: &ConfigMap, new_config: &mut ConfigMap, ) { - for key in migration_config.update.keys() { + for key in to_update.keys() { if !schema.config.whitelist.contains(key) { debug!("Key `{}` not in whitelist, skipping", key); continue; } // If also deleting, delete takes precedence - if migration_config.delete.contains(key) { + if to_delete.contains(key) { continue; } - if let Some(future) = migration_config.update.get(key) { + if let Some(future) = to_update.get(key) { if !config_json.contains_key(key) { info!("Key `{}` not found, will insert", key); new_config.insert(key.to_string(), future.clone()); @@ -66,11 +88,11 @@ fn handle_update_directives( fn handle_delete_directives( schema: &OsConfigSchema, - migration_config: &ConfigMigrationInstructions, + to_delete: &Vec, config_json: &ConfigMap, new_config: &mut ConfigMap, ) { - for key in migration_config.delete.iter() { + for key in to_delete.iter() { if !schema.config.whitelist.contains(key) { debug!("Key `{}` not in whitelist, skipping", key); continue; @@ -123,16 +145,18 @@ mod tests { let configuration = unindent::unindent( r#" { - "update": { + "overrides": { "deadbeef": 2, "deadca1f": "3", "deadca2f": false, "deadca3f": "string0", "deadca4f": "new_field", "not_on_whitelist1": "not_on_whitelist", - "on_both_lists": "on_both_lists2" - }, - "delete": ["deadca5f", "not_on_whitelist2", "on_both_lists"] + "on_both_lists": "on_both_lists2", + "deadca5f": null, + "not_on_whitelist2": null, + "on_both_lists": null + } } "#, ); diff --git a/src/remote.rs b/src/remote.rs index b9c399b..9768a26 100644 --- a/src/remote.rs +++ b/src/remote.rs @@ -12,8 +12,7 @@ pub struct RemoteConfiguration { #[derive(Debug, Serialize, Deserialize, PartialEq)] pub struct ConfigMigrationInstructions { - pub update: HashMap, - pub delete: Vec, + pub overrides: HashMap, } impl RemoteConfiguration { @@ -158,12 +157,10 @@ mod tests { } }, "config": { - "update": { - "logsEndpoint": "https://logs.balenadev.io" - }, - "delete": [ - "pubnubSubscribeKey" - ] + "overrides": { + "logsEndpoint": "https://logs.balenadev.io", + "pubnubSubscribeKey": null + } } }"#; @@ -184,10 +181,10 @@ mod tests { } }, config: ConfigMigrationInstructions { - update: hashmap! { - "logsEndpoint".into() => "https://logs.balenadev.io".into() + overrides: hashmap! { + "logsEndpoint".into() => "https://logs.balenadev.io".into(), + "pubnubSubscribeKey".into() => serde_json::Value::Null }, - delete: vec!["pubnubSubscribeKey".into()], }, }; diff --git a/tests/integration.rs b/tests/integration.rs index 6a96900..b6a55ff 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -102,8 +102,7 @@ fn join() { } }, "config": { - "update": {}, - "delete": [] + "overrides": {} } } "#, @@ -281,8 +280,7 @@ fn join_flasher() { } }, "config": { - "update": {}, - "delete": [] + "overrides": {} } } "#, @@ -418,8 +416,7 @@ fn join_with_root_certificate() { "services": { }, "config": { - "update": {}, - "delete": [] + "overrides": {} } } "#, @@ -643,8 +640,7 @@ fn reconfigure() { "services": { }, "config": { - "update": {}, - "delete": [] + "overrides": {} } } "#, @@ -792,8 +788,7 @@ fn reconfigure_stored() { "services": { }, "config": { - "update": {}, - "delete": [] + "overrides": {} } } "#, @@ -996,8 +991,7 @@ fn update() { } }, "config": { - "update": {}, - "delete": [] + "overrides": {} } } "#, @@ -1163,8 +1157,7 @@ fn update_no_config_changes() { } }, "config": { - "update": {}, - "delete": [] + "overrides": {} } } "#, @@ -1269,8 +1262,7 @@ fn update_with_root_certificate() { "services": { }, "config": { - "update": {}, - "delete": [] + "overrides": {} } } "#, @@ -1335,8 +1327,7 @@ fn update_unmanaged() { "services": { }, "config": { - "update": {}, - "delete": [] + "overrides": {} } } "#, @@ -1436,8 +1427,7 @@ fn leave() { } }, "config": { - "update": {}, - "delete": [] + "overrides": {} } } "#, @@ -1536,8 +1526,7 @@ fn leave_unmanaged() { "services": { }, "config": { - "update": {}, - "delete": [] + "overrides": {} } } "#, @@ -1905,15 +1894,15 @@ fn migrate_config_json() { // - deadbeef: value changed, should update (stringified u64) // - vpnEndpoint: on whitelist & in update, should delete and not update // Delete: - // - apiKey: not on whitelist, should skip - // - mixpanelToken: on whitelist, should delete - // - vpnEndpoint: on whitelist & in update, should delete and not update + // - apiKey: not on whitelist, is null, should skip + // - mixpanelToken: on whitelist, is null, should delete + // - vpnEndpoint: on whitelist, is null AND has value, delete takes precedence let configuration = unindent::unindent( r#" { "services": {}, "config": { - "update": { + "overrides": { "apiEndpoint": "http://api.balenadev.io", "logsEndpoint": "https://logs.balenadev.io", "registryEndpoint": "registry2.balenadev.io", @@ -1921,9 +1910,11 @@ fn migrate_config_json() { "applicationId": 1234568, "persistentLogging": true, "deadbeef": "1234567890", - "vpnEndpoint": "vpn2.balenadev.io" - }, - "delete": ["apiKey", "mixpanelToken", "vpnEndpoint"] + "vpnEndpoint": "vpn2.balenadev.io", + "apiKey": null, + "mixpanelToken": null, + "vpnEndpoint": null + } } } "#,