Skip to content

Commit

Permalink
Migrate existing functionality to os-config schema v2
Browse files Browse the repository at this point in the history
This also includes the binary querying /os/v2/config instead,
which has a different response format. The changes to the response
format 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: balena-os/meta-balena#3227
See: balena-io/open-balena-api#1394
Signed-off-by: Christina Ying Wang <[email protected]>
Change-type: major
  • Loading branch information
cywang117 committed Aug 28, 2023
1 parent 4b99cc7 commit 0ee4720
Show file tree
Hide file tree
Showing 7 changed files with 161 additions and 99 deletions.
2 changes: 1 addition & 1 deletion src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down
2 changes: 1 addition & 1 deletion src/join.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
2 changes: 1 addition & 1 deletion src/leave.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
30 changes: 22 additions & 8 deletions src/remote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String, HashMap<String, String>>,
pub schema_version: String,
pub config: ConfigMigrationInstructions,
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
pub struct ConfigMigrationInstructions {
pub update: HashMap<String, serde_json::Value>,
pub delete: Vec<String>,
}

impl Configuration {
Expand Down Expand Up @@ -66,8 +71,6 @@ fn fetch_configuration_impl(

info!("Service configuration retrieved");

validate_schema_version(&json_data)?;

Ok(serde_json::from_str(&json_data)?)
}

Expand Down Expand Up @@ -141,7 +144,6 @@ fn build_reqwest_client(
mod tests {

use super::*;
use crate::schema::SCHEMA_VERSION;

const JSON_DATA: &str = r#"{
"services": {
Expand All @@ -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 {
Expand All @@ -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);
Expand Down
55 changes: 19 additions & 36 deletions src/schema.rs
Original file line number Diff line number Diff line change
@@ -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<Service>,
pub keys: Vec<String>,
pub schema_version: String,
pub config: ConfigJsonSchema,
}

#[derive(Debug, Serialize, Deserialize, PartialEq)]
Expand All @@ -28,40 +23,24 @@ 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<String>,
// Fields that should be removed when leaving a cloud env (`balena leave`)
pub leave: Vec<String>,
}

pub fn read_os_config_schema(os_config_path: &Path) -> Result<OsConfigSchema> {
read_os_config_schema_impl(os_config_path).context("Reading `os-config.json` schema failed")
}

fn read_os_config_schema_impl(os_config_path: &Path) -> Result<OsConfigSchema> {
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 {

Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion test_utils/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 || {
Expand Down
Loading

0 comments on commit 0ee4720

Please sign in to comment.