diff --git a/src/args.rs b/src/args.rs index ff40825..89b323d 100644 --- a/src/args.rs +++ b/src/args.rs @@ -7,7 +7,7 @@ use crate::systemd::service_exists; pub const SUPERVISOR_SERVICE: &str = "balena-supervisor.service"; -const CONFIG_ROUTE: &str = "/os/v1/config"; +const CONFIG_ROUTE: &str = "/os/v2/config"; const OS_CONFIG_PATH: &str = "/etc/os-config.json"; const CONFIG_JSON_PATH: &str = "/mnt/boot/config.json"; const CONFIG_JSON_FLASHER_PATH: &str = "/tmp/config.json"; diff --git a/src/join.rs b/src/join.rs index 4577137..52adaf0 100644 --- a/src/join.rs +++ b/src/join.rs @@ -149,7 +149,7 @@ fn get_config_contents(path: &str) -> String { } fn clean_config_json_keys(config_json: &mut ConfigMap, schema: &OsConfigSchema) { - for key in &schema.keys { + for key in &schema.config.leave { config_json.remove(key); } } diff --git a/src/leave.rs b/src/leave.rs index 6555056..52622c5 100644 --- a/src/leave.rs +++ b/src/leave.rs @@ -72,7 +72,7 @@ fn delete_config_json_keys( ) -> Result<()> { info!("Deleting config.json keys"); - for key in &schema.keys { + for key in &schema.config.leave { config_json.remove(key); } diff --git a/src/remote.rs b/src/remote.rs index f1f9378..e0a585e 100644 --- a/src/remote.rs +++ b/src/remote.rs @@ -2,13 +2,18 @@ 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 Configuration { pub services: HashMap>, - pub schema_version: String, + pub config: ConfigMigrationInstructions, +} + +#[derive(Debug, Serialize, Deserialize, PartialEq)] +pub struct ConfigMigrationInstructions { + pub update: HashMap, + pub delete: Vec, } impl Configuration { @@ -66,8 +71,6 @@ fn fetch_configuration_impl( info!("Service configuration retrieved"); - validate_schema_version(&json_data)?; - Ok(serde_json::from_str(&json_data)?) } @@ -141,7 +144,6 @@ fn build_reqwest_client( mod tests { use super::*; - use crate::schema::SCHEMA_VERSION; const JSON_DATA: &str = r#"{ "services": { @@ -155,11 +157,18 @@ mod tests { "authorized_keys": "authorized keys here" } }, - "schema_version": "1.0.0" + "config": { + "update": { + "logsEndpoint": "https://logs.balenadev.io" + }, + "delete": [ + "pubnubSubscribeKey" + ] + } }"#; #[test] - fn parse_configuration_v1() { + fn parse_configuration() { let parsed: Configuration = serde_json::from_str(JSON_DATA).unwrap(); let expected = Configuration { @@ -174,7 +183,12 @@ mod tests { "authorized_keys".into() => "authorized keys here".into() } }, - schema_version: SCHEMA_VERSION.into(), + config: ConfigMigrationInstructions { + update: hashmap! { + "logsEndpoint".into() => "https://logs.balenadev.io".into() + }, + delete: vec!["pubnubSubscribeKey".into()], + }, }; assert_eq!(parsed, expected); diff --git a/src/schema.rs b/src/schema.rs index 0ed83fc..d1b6d43 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -1,18 +1,13 @@ 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"; +use anyhow::{Context, Result}; #[derive(Debug, Serialize, Deserialize, PartialEq)] pub struct OsConfigSchema { pub services: Vec, - pub keys: Vec, - pub schema_version: String, + pub config: ConfigJsonSchema, } #[derive(Debug, Serialize, Deserialize, PartialEq)] @@ -28,6 +23,14 @@ 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, + // Fields that should be removed when leaving a cloud env (`balena leave`) + pub leave: 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 +38,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 { @@ -97,12 +76,14 @@ mod tests { } ], - "keys": ["apiKey", "apiEndpoint", "vpnEndpoint"], - "schema_version": "1.0.0" + "config": { + "whitelist": ["logsEndpoint"], + "leave": ["apiKey", "apiEndpoint", "vpnEndpoint"] + } }"#; #[test] - fn parse_os_config_v1() { + fn parse_os_config() { let parsed: OsConfigSchema = serde_json::from_str(JSON_DATA).unwrap(); let expected = OsConfigSchema { @@ -132,8 +113,10 @@ mod tests { systemd_services: vec![], }, ], - keys: vec!["apiKey".into(), "apiEndpoint".into(), "vpnEndpoint".into()], - schema_version: SCHEMA_VERSION.into(), + config: ConfigJsonSchema { + whitelist: vec!["logsEndpoint".into()], + leave: vec!["apiKey".into(), "apiEndpoint".into(), "vpnEndpoint".into()], + }, }; assert_eq!(parsed, expected); diff --git a/test_utils/src/lib.rs b/test_utils/src/lib.rs index 8599a7a..d8612b0 100644 --- a/test_utils/src/lib.rs +++ b/test_utils/src/lib.rs @@ -22,7 +22,7 @@ use base64::Engine; * Mock JSON HTTP server */ -const CONFIG_ROUTE: &str = "/os/v1/config"; +const CONFIG_ROUTE: &str = "/os/v2/config"; pub fn serve_config(config: String, with_ssl: bool, port: u16) -> Serve { let mut server = HttpServer::new(move || { diff --git a/tests/integration.rs b/tests/integration.rs index d83a526..571932e 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -76,8 +76,10 @@ fn join() { }} ], - "keys": ["apiKey", "apiEndpoint", "vpnEndpoint"], - "schema_version": "1.0.0" + "config": {{ + "whitelist": ["logsEndpoint"], + "leave": ["apiKey", "apiEndpoint", "vpnEndpoint"] + }} }} "# ); @@ -99,7 +101,10 @@ fn join() { "mock-3": "MOCK-3-0123456789" } }, - "schema_version": "1.0.0" + "config": { + "update": {}, + "delete": [] + } } "#, ); @@ -133,7 +138,7 @@ fn join() { let output = unindent::unindent(&format!( r#" - Fetching service configuration from http://localhost:{port}/os/v1/config... + Fetching service configuration from http://localhost:{port}/os/v2/config... Service configuration retrieved Stopping balena-supervisor.service... Awaiting balena-supervisor.service to exit... @@ -256,8 +261,10 @@ fn join_flasher() { "systemd_services": ["mock-service-1.service"] }} ], - "keys": ["apiKey", "apiEndpoint", "vpnEndpoint"], - "schema_version": "1.0.0" + "config": {{ + "whitelist": ["logsEndpoint"], + "leave": ["apiKey", "apiEndpoint", "vpnEndpoint"] + }} }} "# ); @@ -272,7 +279,10 @@ fn join_flasher() { "mock-1": "MOCK-1-АБВГДЕЖЗИЙ" } }, - "schema_version": "1.0.0" + "config": { + "update": {}, + "delete": [] + } } "#, ); @@ -306,7 +316,7 @@ fn join_flasher() { let output = unindent::unindent(&format!( r#" - Fetching service configuration from http://localhost:{port}/os/v1/config... + Fetching service configuration from http://localhost:{port}/os/v2/config... Service configuration retrieved Stopping balena-supervisor.service... Awaiting balena-supervisor.service to exit... @@ -391,8 +401,10 @@ fn join_with_root_certificate() { { "services": [ ], - "keys": ["apiKey", "apiEndpoint", "vpnEndpoint"], - "schema_version": "1.0.0" + "config": { + "whitelist": ["logsEndpoint"], + "leave": ["apiKey", "apiEndpoint", "vpnEndpoint"] + } } "#; @@ -403,7 +415,10 @@ fn join_with_root_certificate() { { "services": { }, - "schema_version": "1.0.0" + "config": { + "update": {}, + "delete": [] + } } "#, ); @@ -439,7 +454,7 @@ fn join_with_root_certificate() { let output = unindent::unindent(&format!( r#" - Fetching service configuration from https://localhost:{port}/os/v1/config... + Fetching service configuration from https://localhost:{port}/os/v2/config... Service configuration retrieved No configuration changes Stopping balena-supervisor.service... @@ -514,8 +529,10 @@ fn incompatible_device_types() { { "services": [ ], - "keys": ["apiKey", "apiEndpoint", "vpnEndpoint"], - "schema_version": "1.0.0" + "config": { + "whitelist": ["logsEndpoint"], + "leave": ["apiKey", "apiEndpoint", "vpnEndpoint"] + } } "#, ); @@ -607,8 +624,10 @@ fn reconfigure() { { "services": [ ], - "keys": ["apiKey", "apiEndpoint", "vpnEndpoint"], - "schema_version": "1.0.0" + "config": { + "whitelist": ["logsEndpoint"], + "leave": ["apiKey", "apiEndpoint", "vpnEndpoint"] + } } "#, ); @@ -620,7 +639,10 @@ fn reconfigure() { { "services": { }, - "schema_version": "1.0.0" + "config": { + "update": {}, + "delete": [] + } } "#, ); @@ -654,7 +676,7 @@ fn reconfigure() { let output = unindent::unindent(&format!( r#" - Fetching service configuration from http://localhost:{port}/os/v1/config... + Fetching service configuration from http://localhost:{port}/os/v2/config... Service configuration retrieved No configuration changes Stopping balena-supervisor.service... @@ -750,8 +772,10 @@ fn reconfigure_stored() { { "services": [ ], - "keys": ["apiKey", "apiEndpoint", "vpnEndpoint"], - "schema_version": "1.0.0" + "config": { + "whitelist": ["logsEndpoint"], + "leave": ["apiKey", "apiEndpoint", "vpnEndpoint"] + } } "#, ); @@ -763,7 +787,10 @@ fn reconfigure_stored() { { "services": { }, - "schema_version": "1.0.0" + "config": { + "update": {}, + "delete": [] + } } "#, ); @@ -797,7 +824,7 @@ fn reconfigure_stored() { let output = unindent::unindent(&format!( r#" - Fetching service configuration from http://localhost:{port}/os/v1/config... + Fetching service configuration from http://localhost:{port}/os/v2/config... Service configuration retrieved No configuration changes Stopping balena-supervisor.service... @@ -934,8 +961,10 @@ fn update() { }} ], - "keys": ["apiKey", "apiEndpoint", "vpnEndpoint"], - "schema_version": "1.0.0" + "config": {{ + "whitelist": ["logsEndpoint"], + "leave": ["apiKey", "apiEndpoint", "vpnEndpoint"] + }} }} "# ); @@ -961,7 +990,10 @@ fn update() { "mock-3": "MOCK-3-0123456789" } }, - "schema_version": "1.0.0" + "config": { + "update": {}, + "delete": [] + } } "#, ); @@ -970,7 +1002,7 @@ fn update() { let output = unindent::unindent(&format!( r#" - Fetching service configuration from http://localhost:{port}/os/v1/config... + Fetching service configuration from http://localhost:{port}/os/v2/config... Service configuration retrieved Stopping balena-supervisor.service... Awaiting balena-supervisor.service to exit... @@ -1096,8 +1128,10 @@ fn update_no_config_changes() { }} ], - "keys": ["apiKey", "apiEndpoint", "vpnEndpoint"], - "schema_version": "1.0.0" + "config": {{ + "whitelist": ["logsEndpoint"], + "leave": ["apiKey", "apiEndpoint", "vpnEndpoint"] + }} }} "# ); @@ -1122,7 +1156,10 @@ fn update_no_config_changes() { "mock-3": "MOCK-3-0123456789" } }, - "schema_version": "1.0.0" + "config": { + "update": {}, + "delete": [] + } } "#, ); @@ -1131,7 +1168,7 @@ fn update_no_config_changes() { let output = unindent::unindent(&format!( r#" - Fetching service configuration from http://localhost:{port}/os/v1/config... + Fetching service configuration from http://localhost:{port}/os/v2/config... Service configuration retrieved No configuration changes "#, @@ -1210,8 +1247,10 @@ fn update_with_root_certificate() { { "services": [ ], - "keys": ["apiKey", "apiEndpoint", "vpnEndpoint"], - "schema_version": "1.0.0" + "config": { + "whitelist": ["logsEndpoint"], + "leave": ["apiKey", "apiEndpoint", "vpnEndpoint"] + } } "#; @@ -1222,7 +1261,10 @@ fn update_with_root_certificate() { { "services": { }, - "schema_version": "1.0.0" + "config": { + "update": {}, + "delete": [] + } } "#, ); @@ -1231,7 +1273,7 @@ fn update_with_root_certificate() { let output = unindent::unindent(&format!( r#" - Fetching service configuration from https://localhost:{port}/os/v1/config... + Fetching service configuration from https://localhost:{port}/os/v2/config... Service configuration retrieved No configuration changes "#, @@ -1270,8 +1312,10 @@ fn update_unmanaged() { { "services": [ ], - "keys": ["apiKey", "apiEndpoint", "vpnEndpoint"], - "schema_version": "1.0.0" + "config": { + "whitelist": ["logsEndpoint"], + "leave": ["apiKey", "apiEndpoint", "vpnEndpoint"] + } } "#; @@ -1282,7 +1326,10 @@ fn update_unmanaged() { { "services": { }, - "schema_version": "1.0.0" + "config": { + "update": {}, + "delete": [] + } } "#, ); @@ -1360,8 +1407,10 @@ fn leave() { "systemd_services": ["mock-service-3.service"] }} ], - "keys": ["apiKey", "apiEndpoint", "vpnEndpoint", "vpnPort", "registryEndpoint", "deltaEndpoint"], - "schema_version": "1.0.0" + "config": {{ + "whitelist": ["logsEndpoint"], + "leave": ["apiKey", "apiEndpoint", "vpnEndpoint", "vpnPort", "registryEndpoint", "deltaEndpoint"] + }} }} "# ); @@ -1378,7 +1427,10 @@ fn leave() { "mock-3": "MOCK-3-0123456789" } }, - "schema_version": "1.0.0" + "config": { + "update": {}, + "delete": [] + } } "#, ); @@ -1460,8 +1512,10 @@ fn leave_unmanaged() { { "services": [ ], - "keys": ["apiKey", "apiEndpoint", "vpnEndpoint"], - "schema_version": "1.0.0" + "config": { + "whitelist": ["logsEndpoint"], + "leave": ["apiKey", "apiEndpoint", "vpnEndpoint"] + } } "#, ); @@ -1473,7 +1527,10 @@ fn leave_unmanaged() { { "services": { }, - "schema_version": "1.0.0" + "config": { + "update": {}, + "delete": [] + } } "#, ); @@ -1516,8 +1573,10 @@ fn generate_api_key_unmanaged() { { "services": [ ], - "keys": ["apiKey", "apiEndpoint", "vpnEndpoint"], - "schema_version": "1.0.0" + "config": { + "whitelist": ["logsEndpoint"], + "leave": ["apiKey", "apiEndpoint", "vpnEndpoint"] + } } "#, ); @@ -1575,8 +1634,10 @@ fn generate_api_key_already_generated() { { "services": [ ], - "keys": ["apiKey", "apiEndpoint", "vpnEndpoint"], - "schema_version": "1.0.0" + "config": { + "whitelist": ["logsEndpoint"], + "leave": ["apiKey", "apiEndpoint", "vpnEndpoint"] + } } "#, ); @@ -1634,8 +1695,10 @@ fn generate_api_key_reuse() { { "services": [ ], - "keys": ["apiKey", "apiEndpoint", "vpnEndpoint"], - "schema_version": "1.0.0" + "config": { + "whitelist": ["logsEndpoint"], + "leave": ["apiKey", "apiEndpoint", "vpnEndpoint"] + } } "#, ); @@ -1716,8 +1779,10 @@ fn generate_api_key_new() { { "services": [ ], - "keys": ["apiKey", "apiEndpoint", "vpnEndpoint"], - "schema_version": "1.0.0" + "config": { + "whitelist": ["logsEndpoint"], + "leave": ["apiKey", "apiEndpoint", "vpnEndpoint"] + } } "#, );