From f0882b960fa8810be462e7c23a64f39b5d1594ae Mon Sep 17 00:00:00 2001 From: Christina Ying Wang Date: Wed, 13 Sep 2023 15:43:23 -0700 Subject: [PATCH] Migrate existing functionality to new os-config schema The response from querying /os/v1/config has changed slightly, the changes to the response are reflected in the updated tests in this commit. See: https://balena.fibery.io/Work/Improvement/os-config-improving-the-interface-for-config.json-modification-901 See: https://github.com/balena-os/meta-balena/pull/3227 See: https://github.com/balena-io/open-balena-api/pull/1394 Signed-off-by: Christina Ying Wang Change-type: minor --- src/remote.rs | 23 ++++++--- src/schema.rs | 50 +++++++------------- tests/integration.rs | 108 ++++++++++++++++++++++++++++++++----------- 3 files changed, 113 insertions(+), 68 deletions(-) diff --git a/src/remote.rs b/src/remote.rs index 752e9c2..2c790e0 100644 --- a/src/remote.rs +++ b/src/remote.rs @@ -2,13 +2,17 @@ use std::collections::HashMap; use std::thread; use std::time::Duration; -use crate::schema::validate_schema_version; use anyhow::{anyhow, Context, Result}; #[derive(Debug, Serialize, Deserialize, PartialEq)] pub struct RemoteConfiguration { pub services: HashMap>, - pub schema_version: String, + pub config: ConfigMigrationInstructions, +} + +#[derive(Debug, Serialize, Deserialize, PartialEq)] +pub struct ConfigMigrationInstructions { + pub overrides: HashMap, } impl RemoteConfiguration { @@ -66,8 +70,6 @@ fn fetch_configuration_impl( info!("Service configuration retrieved"); - validate_schema_version(&json_data)?; - Ok(serde_json::from_str(&json_data)?) } @@ -141,7 +143,6 @@ fn build_reqwest_client( mod tests { use super::*; - use crate::schema::SCHEMA_VERSION; const JSON_DATA: &str = r#"{ "services": { @@ -155,7 +156,11 @@ mod tests { "authorized_keys": "authorized keys here" } }, - "schema_version": "1.0.0" + "config": { + "overrides": { + "logsEndpoint": "https://logs.balenadev.io" + } + } }"#; #[test] @@ -174,7 +179,11 @@ mod tests { "authorized_keys".into() => "authorized keys here".into() } }, - schema_version: SCHEMA_VERSION.into(), + config: ConfigMigrationInstructions { + overrides: hashmap! { + "logsEndpoint".into() => "https://logs.balenadev.io".into() + }, + }, }; assert_eq!(parsed, expected); diff --git a/src/schema.rs b/src/schema.rs index 0ed83fc..fba2651 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -1,18 +1,14 @@ +use crate::fs::read_file; +use anyhow::{Context, Result}; use std::collections::HashMap; use std::path::Path; -use serde_json::Value; - -use crate::fs::read_file; -use anyhow::{bail, Context, Result}; - -pub const SCHEMA_VERSION: &str = "1.0.0"; - #[derive(Debug, Serialize, Deserialize, PartialEq)] pub struct OsConfigSchema { pub services: Vec, + // Fields that should be removed from config.json when leaving a cloud env (`balena leave`) pub keys: Vec, - pub schema_version: String, + pub config: ConfigJsonSchema, } #[derive(Debug, Serialize, Deserialize, PartialEq)] @@ -28,6 +24,12 @@ pub struct ConfigFile { pub perm: String, } +#[derive(Debug, Serialize, Deserialize, PartialEq)] +pub struct ConfigJsonSchema { + // Fields that may be modified in config.json + pub whitelist: Vec, +} + pub fn read_os_config_schema(os_config_path: &Path) -> Result { read_os_config_schema_impl(os_config_path).context("Reading `os-config.json` schema failed") } @@ -35,33 +37,9 @@ pub fn read_os_config_schema(os_config_path: &Path) -> Result { fn read_os_config_schema_impl(os_config_path: &Path) -> Result { let json_data = read_file(os_config_path)?; - validate_schema_version(&json_data)?; - Ok(serde_json::from_str(&json_data)?) } -pub fn validate_schema_version(json_data: &str) -> Result<()> { - let parsed: Value = serde_json::from_str(json_data)?; - - match parsed.get("schema_version") { - Some(version_value) => match version_value.as_str() { - Some(schema_version) => { - if schema_version == SCHEMA_VERSION { - Ok(()) - } else { - bail!( - "Expected schema version {}, got {}", - SCHEMA_VERSION, - schema_version - ) - } - } - _ => bail!("`schema_version` should be a string"), - }, - _ => bail!("Missing `schema_version`"), - } -} - #[cfg(test)] mod tests { @@ -98,7 +76,9 @@ mod tests { } ], "keys": ["apiKey", "apiEndpoint", "vpnEndpoint"], - "schema_version": "1.0.0" + "config": { + "whitelist": ["logsEndpoint"] + } }"#; #[test] @@ -133,7 +113,9 @@ mod tests { }, ], keys: vec!["apiKey".into(), "apiEndpoint".into(), "vpnEndpoint".into()], - schema_version: SCHEMA_VERSION.into(), + config: ConfigJsonSchema { + whitelist: vec!["logsEndpoint".into()], + }, }; assert_eq!(parsed, expected); diff --git a/tests/integration.rs b/tests/integration.rs index d83a526..d0e5022 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -77,7 +77,9 @@ fn join() { }} ], "keys": ["apiKey", "apiEndpoint", "vpnEndpoint"], - "schema_version": "1.0.0" + "config": {{ + "whitelist": ["logsEndpoint"] + }} }} "# ); @@ -99,7 +101,9 @@ fn join() { "mock-3": "MOCK-3-0123456789" } }, - "schema_version": "1.0.0" + "config": { + "overrides": {} + } } "#, ); @@ -257,7 +261,9 @@ fn join_flasher() { }} ], "keys": ["apiKey", "apiEndpoint", "vpnEndpoint"], - "schema_version": "1.0.0" + "config": {{ + "whitelist": ["logsEndpoint"] + }} }} "# ); @@ -272,7 +278,9 @@ fn join_flasher() { "mock-1": "MOCK-1-АБВГДЕЖЗИЙ" } }, - "schema_version": "1.0.0" + "config": { + "overrides": {} + } } "#, ); @@ -392,7 +400,9 @@ fn join_with_root_certificate() { "services": [ ], "keys": ["apiKey", "apiEndpoint", "vpnEndpoint"], - "schema_version": "1.0.0" + "config": { + "whitelist": ["logsEndpoint"] + } } "#; @@ -403,7 +413,9 @@ fn join_with_root_certificate() { { "services": { }, - "schema_version": "1.0.0" + "config": { + "overrides": {} + } } "#, ); @@ -515,7 +527,9 @@ fn incompatible_device_types() { "services": [ ], "keys": ["apiKey", "apiEndpoint", "vpnEndpoint"], - "schema_version": "1.0.0" + "config": { + "whitelist": ["logsEndpoint"] + } } "#, ); @@ -608,7 +622,9 @@ fn reconfigure() { "services": [ ], "keys": ["apiKey", "apiEndpoint", "vpnEndpoint"], - "schema_version": "1.0.0" + "config": { + "whitelist": ["logsEndpoint"] + } } "#, ); @@ -620,7 +636,9 @@ fn reconfigure() { { "services": { }, - "schema_version": "1.0.0" + "config": { + "overrides": {} + } } "#, ); @@ -751,7 +769,9 @@ fn reconfigure_stored() { "services": [ ], "keys": ["apiKey", "apiEndpoint", "vpnEndpoint"], - "schema_version": "1.0.0" + "config": { + "whitelist": ["logsEndpoint"] + } } "#, ); @@ -763,7 +783,9 @@ fn reconfigure_stored() { { "services": { }, - "schema_version": "1.0.0" + "config": { + "overrides": {} + } } "#, ); @@ -935,7 +957,9 @@ fn update() { }} ], "keys": ["apiKey", "apiEndpoint", "vpnEndpoint"], - "schema_version": "1.0.0" + "config": {{ + "whitelist": ["logsEndpoint"] + }} }} "# ); @@ -961,7 +985,9 @@ fn update() { "mock-3": "MOCK-3-0123456789" } }, - "schema_version": "1.0.0" + "config": { + "overrides": {} + } } "#, ); @@ -1097,7 +1123,9 @@ fn update_no_config_changes() { }} ], "keys": ["apiKey", "apiEndpoint", "vpnEndpoint"], - "schema_version": "1.0.0" + "config": {{ + "whitelist": ["logsEndpoint"] + }} }} "# ); @@ -1122,7 +1150,9 @@ fn update_no_config_changes() { "mock-3": "MOCK-3-0123456789" } }, - "schema_version": "1.0.0" + "config": { + "overrides": {} + } } "#, ); @@ -1211,7 +1241,9 @@ fn update_with_root_certificate() { "services": [ ], "keys": ["apiKey", "apiEndpoint", "vpnEndpoint"], - "schema_version": "1.0.0" + "config": { + "whitelist": ["logsEndpoint"] + } } "#; @@ -1222,7 +1254,9 @@ fn update_with_root_certificate() { { "services": { }, - "schema_version": "1.0.0" + "config": { + "overrides": {} + } } "#, ); @@ -1271,7 +1305,9 @@ fn update_unmanaged() { "services": [ ], "keys": ["apiKey", "apiEndpoint", "vpnEndpoint"], - "schema_version": "1.0.0" + "config": { + "whitelist": ["logsEndpoint"] + } } "#; @@ -1282,7 +1318,9 @@ fn update_unmanaged() { { "services": { }, - "schema_version": "1.0.0" + "config": { + "overrides": {} + } } "#, ); @@ -1361,7 +1399,9 @@ fn leave() { }} ], "keys": ["apiKey", "apiEndpoint", "vpnEndpoint", "vpnPort", "registryEndpoint", "deltaEndpoint"], - "schema_version": "1.0.0" + "config": {{ + "whitelist": ["logsEndpoint"] + }} }} "# ); @@ -1378,7 +1418,9 @@ fn leave() { "mock-3": "MOCK-3-0123456789" } }, - "schema_version": "1.0.0" + "config": { + "overrides": {} + } } "#, ); @@ -1461,7 +1503,9 @@ fn leave_unmanaged() { "services": [ ], "keys": ["apiKey", "apiEndpoint", "vpnEndpoint"], - "schema_version": "1.0.0" + "config": { + "whitelist": ["logsEndpoint"] + } } "#, ); @@ -1473,7 +1517,9 @@ fn leave_unmanaged() { { "services": { }, - "schema_version": "1.0.0" + "config": { + "overrides": {} + } } "#, ); @@ -1517,7 +1563,9 @@ fn generate_api_key_unmanaged() { "services": [ ], "keys": ["apiKey", "apiEndpoint", "vpnEndpoint"], - "schema_version": "1.0.0" + "config": { + "whitelist": ["logsEndpoint"] + } } "#, ); @@ -1576,7 +1624,9 @@ fn generate_api_key_already_generated() { "services": [ ], "keys": ["apiKey", "apiEndpoint", "vpnEndpoint"], - "schema_version": "1.0.0" + "config": { + "whitelist": ["logsEndpoint"] + } } "#, ); @@ -1635,7 +1685,9 @@ fn generate_api_key_reuse() { "services": [ ], "keys": ["apiKey", "apiEndpoint", "vpnEndpoint"], - "schema_version": "1.0.0" + "config": { + "whitelist": ["logsEndpoint"] + } } "#, ); @@ -1717,7 +1769,9 @@ fn generate_api_key_new() { "services": [ ], "keys": ["apiKey", "apiEndpoint", "vpnEndpoint"], - "schema_version": "1.0.0" + "config": { + "whitelist": ["logsEndpoint"] + } } "#, );