diff --git a/src/kernel_sdk/CHANGES.md b/src/kernel_sdk/CHANGES.md index cba82da8ff90157b375905c228cf5fec492a5334..4b79f3e9db89559c8dbe017922e65b8f718f9800 100644 --- a/src/kernel_sdk/CHANGES.md +++ b/src/kernel_sdk/CHANGES.md @@ -37,6 +37,7 @@ ### Installer client/kernel - Add support for using the `set` instruction with large (> 512 byte) values. +- Add `merge-setup-files` subcommand to merge multiple configuration files in one. ## Version 0.2.2 diff --git a/src/kernel_sdk/installer-client/src/commands.rs b/src/kernel_sdk/installer-client/src/commands.rs index a7c5df673aac98d448f8f98dd838493986ead98d..221c6276deba76c0243472f920c85cf472d349d1 100644 --- a/src/kernel_sdk/installer-client/src/commands.rs +++ b/src/kernel_sdk/installer-client/src/commands.rs @@ -1,4 +1,5 @@ // SPDX-FileCopyrightText: 2023 TriliTech +// SPDX-FileCopyrightText: 2025 Functori // // SPDX-License-Identifier: MIT @@ -30,4 +31,11 @@ pub enum Commands { #[arg(short, long, value_name = "DISPLAY_ROOT_HASH")] display_root_hash: bool, }, + MergeSetupFiles { + #[arg(short, long, value_name = "OUTPUT_SETUP_FILE")] + output: OsString, + + #[arg(short = 'S', long, value_name = "INSTALLER_SETUP_CONFIGS", num_args = 1.., required = true)] + setup_files: Vec, + }, } diff --git a/src/kernel_sdk/installer-client/src/config.rs b/src/kernel_sdk/installer-client/src/config.rs index 336612d748aeca4434de30a49d9f37ee4022badc..52be1dafefc67c28fefbc8c71d4453af0275eaad 100644 --- a/src/kernel_sdk/installer-client/src/config.rs +++ b/src/kernel_sdk/installer-client/src/config.rs @@ -1,4 +1,5 @@ // SPDX-FileCopyrightText: 2023-2024 TriliTech +// SPDX-FileCopyrightText: 2025 Functori // // SPDX-License-Identifier: MIT @@ -19,6 +20,8 @@ pub enum ConfigurationError { FileNotFound(std::io::Error), #[error("Unable to parse config file: {0}.")] ParseError(serde_yaml::Error), + #[error("Unable to serialize yaml file: {0}.")] + SerializeError(serde_yaml::Error), #[error("Unable to convert config to a valid program: {0}.")] YamlConfigInvalid(#[from] ConfigConversionError), } @@ -29,6 +32,29 @@ const PREPARE_KERNEL_PATH: RefPath = RefPath::assert_from(b"/installer/kernel/bo // Path of currently running kernel. const KERNEL_BOOT_PATH: RefPath = RefPath::assert_from(b"/kernel/boot.wasm"); +fn setup_file_to_config(setup_file: &OsString) -> Result { + let setup_file = + File::open(Path::new(&setup_file)).map_err(ConfigurationError::FileNotFound)?; + let yaml_config: YamlConfig = + YamlConfig::from_reader(setup_file).map_err(ConfigurationError::ParseError)?; + Ok(yaml_config) +} + +pub fn merge_install_configs( + setup_files: Vec, +) -> Result { + let mut merged_yaml_instructions = vec![]; + for setup_file in setup_files { + let yaml = setup_file_to_config(&setup_file)?; + merged_yaml_instructions.extend(yaml.instructions); + } + + let merged_yaml_config = YamlConfig { + instructions: merged_yaml_instructions, + }; + serde_yaml::to_string(&merged_yaml_config).map_err(ConfigurationError::SerializeError) +} + pub fn create_installer_config( root_hash: PreimageHash, setup_file: Option, @@ -52,10 +78,7 @@ pub fn create_installer_config( let setup_program: OwnedConfigProgram = match setup_file { None => OwnedConfigProgram(vec![]), Some(setup_file) => { - let setup_file = File::open(Path::new(&setup_file)) - .map_err(ConfigurationError::FileNotFound)?; - let yaml_config: YamlConfig = YamlConfig::from_reader(setup_file) - .map_err(ConfigurationError::ParseError)?; + let yaml_config = setup_file_to_config(&setup_file)?; yaml_config.to_config_program(content_to_config)? } }; diff --git a/src/kernel_sdk/installer-client/src/main.rs b/src/kernel_sdk/installer-client/src/main.rs index 2ef3551ea65cb6612d4ea7ad0cae01a8ecb066d0..1760fc7f4979440aecc6cb3f2b807580274fa764 100644 --- a/src/kernel_sdk/installer-client/src/main.rs +++ b/src/kernel_sdk/installer-client/src/main.rs @@ -1,4 +1,5 @@ // SPDX-FileCopyrightText: 2023-2024 TriliTech +// SPDX-FileCopyrightText: 2025 Functori // // SPDX-License-Identifier: MIT @@ -10,8 +11,11 @@ mod preimages; use clap::Parser; use commands::Cli; use commands::Commands; +use std::fs::write; use std::path::Path; -use tezos_smart_rollup_installer::config::{create_installer_config, ConfigurationError}; +use tezos_smart_rollup_installer::config::{ + create_installer_config, merge_install_configs, ConfigurationError, +}; use thiserror::Error; fn main() -> Result<(), ClientError> { @@ -43,6 +47,15 @@ fn main() -> Result<(), ClientError> { println!("ROOT_HASH: {}", root_hash_hex); }; } + Commands::MergeSetupFiles { + output, + setup_files, + } => { + let output_path = Path::new(&output); + let encoded_yaml_config = merge_install_configs(setup_files)?; + write(output_path, encoded_yaml_config) + .map_err(ClientError::SaveMergedConfig)?; + } } Ok(()) @@ -56,4 +69,6 @@ enum ClientError { ConfigError(#[from] ConfigurationError), #[error("Unable to save installer kernel: {0}")] SaveInstaller(std::io::Error), + #[error("Unable to save the merged config: {0}")] + SaveMergedConfig(std::io::Error), } diff --git a/src/kernel_sdk/installer-client/tests/installer_config.rs b/src/kernel_sdk/installer-client/tests/installer_config.rs index 4ad75e17ed02321decbdacd2a399a3247e5dfe77..50aa6813cbd5aeb808fecc1ced4a47e87659f69f 100644 --- a/src/kernel_sdk/installer-client/tests/installer_config.rs +++ b/src/kernel_sdk/installer-client/tests/installer_config.rs @@ -1,10 +1,11 @@ // SPDX-FileCopyrightText: 2023-2024 TriliTech +// SPDX-FileCopyrightText: 2025 Functori // SPDX-FileCopyrightText: 2023 Nomadic Labs // // SPDX-License-Identifier: MIT use std::ffi::OsString; -use std::fs; +use std::fs::{self, write}; use std::path::Path; use tezos_smart_rollup::core_unsafe::MAX_FILE_CHUNK_SIZE; use tezos_smart_rollup::dac::pages::prepare_preimages; @@ -12,12 +13,15 @@ use tezos_smart_rollup::dac::PreimageHash; use tezos_smart_rollup::host::Runtime; use tezos_smart_rollup_host::path::{OwnedPath, RefPath}; use tezos_smart_rollup_host::runtime::RuntimeError; -use tezos_smart_rollup_installer::config::create_installer_config; +use tezos_smart_rollup_installer::config::{ + create_installer_config, merge_install_configs, +}; use tezos_smart_rollup_installer::installer::with_config_program; use tezos_smart_rollup_installer::KERNEL_BOOT_PATH; use tezos_smart_rollup_installer_config::binary::owned::{ OwnedBytes, OwnedConfigInstruction, OwnedConfigProgram, }; +use tezos_smart_rollup_installer_config::yaml::YamlConfig; use tezos_smart_rollup_mock::MockHost; fn write_kernel_to_boot_path(host: &mut MockHost, kernel: Vec) { @@ -178,3 +182,37 @@ fn yaml_config_set_execute() { assert_eq!(expected, actual) } + +#[test] +fn merge_yaml_configurations_produce_expected_kernel() { + // Merge move and set configuration + let encoded_setup = merge_install_configs(vec![ + OsString::from("tests/resources/move_config.yaml"), + OsString::from("tests/resources/set_config.yaml"), + ]) + .unwrap(); + + // Verify that the deserialized yaml is valid + let _yaml_config = YamlConfig::from_string(&encoded_setup).unwrap(); + + let merged_config = "/tmp/move_and_set_config.yaml"; + + write(merged_config, encoded_setup).unwrap(); + + let mut host = MockHost::default(); + + // Test that the new merged configuration produce the expected kernel + let original_kernel = kernel_from_setup_file(&mut host, merged_config); + + let temporary_path = RefPath::assert_from(b"/temporary/kernel/boot.wasm"); + let boot_kernel = host.store_read_all(&temporary_path).unwrap(); + assert_eq!(original_kernel, boot_kernel); + + let to = RefPath::assert_from(b"/tmp/foo"); + let expected = String::from("Un festival de GADT"); + + let encoded_string = host.store_read_all(&to).unwrap(); + let actual = String::from_utf8(encoded_string).unwrap(); + + assert_eq!(expected, actual) +}