diff --git a/sources/Cargo.lock b/sources/Cargo.lock index 49972352880..32b96198602 100644 --- a/sources/Cargo.lock +++ b/sources/Cargo.lock @@ -2101,17 +2101,16 @@ dependencies = [ name = "host-containers" version = "0.1.0" dependencies = [ - "apiclient", "base64 0.13.1", "constants", "generate-readme", - "http", "log", - "models", - "serde_json", + "model-derive", + "modeled-types", + "serde", "simplelog", "snafu", - "tokio", + "toml 0.8.8", ] [[package]] diff --git a/sources/api/host-containers/Cargo.toml b/sources/api/host-containers/Cargo.toml index e86651e0ea0..f23b0aab5b8 100644 --- a/sources/api/host-containers/Cargo.toml +++ b/sources/api/host-containers/Cargo.toml @@ -10,16 +10,15 @@ build = "build.rs" exclude = ["README.md"] [dependencies] -apiclient = { path = "../apiclient", version = "0.1" } base64 = "0.13" constants = { path = "../../constants", version = "0.1" } -http = "0.2" log = "0.4" -models = { path = "../../models", version = "0.1" } -serde_json = "1" +modeled-types = { path = "../../models/modeled-types", version = "0.1" } +model-derive = { path = "../../models/model-derive", version = "0.1" } +serde = { version = "1", features = ["derive"] } simplelog = "0.12" snafu = "0.7" -tokio = { version = "~1.32", default-features = false, features = ["macros", "rt-multi-thread"] } # LTS +toml = "0.8" [build-dependencies] generate-readme = { version = "0.1", path = "../../generate-readme" } diff --git a/sources/api/host-containers/README.md b/sources/api/host-containers/README.md index 1575be374b3..6707e4d4095 100644 --- a/sources/api/host-containers/README.md +++ b/sources/api/host-containers/README.md @@ -6,7 +6,7 @@ Current version: 0.1.0 host-containers ensures that host containers are running as defined in system settings. -It queries the API for their settings, then configures the system by: +It reads the currently configured containers from its config file, then configures the system by: * creating a user-data file in the host container's persistent storage area, if a base64-encoded user-data setting is set for the host container. (The decoded contents are available to the container at /.bottlerocket/host-containers/NAME/user-data) diff --git a/sources/api/host-containers/src/config.rs b/sources/api/host-containers/src/config.rs new file mode 100644 index 00000000000..74f79f41401 --- /dev/null +++ b/sources/api/host-containers/src/config.rs @@ -0,0 +1,16 @@ +use model_derive::model; +use modeled_types::{Identifier, Url, ValidBase64}; +use std::collections::HashMap; + +#[model] +struct HostContainersConfig { + host_containers: HashMap, +} + +#[model] +struct HostContainer { + source: Url, + enabled: bool, + superpowered: bool, + user_data: ValidBase64, +} diff --git a/sources/api/host-containers/src/main.rs b/sources/api/host-containers/src/main.rs index 25bf7222976..6cfb0ee7d00 100644 --- a/sources/api/host-containers/src/main.rs +++ b/sources/api/host-containers/src/main.rs @@ -3,7 +3,7 @@ host-containers ensures that host containers are running as defined in system settings. -It queries the API for their settings, then configures the system by: +It reads the currently configured containers from its config file, then configures the system by: * creating a user-data file in the host container's persistent storage area, if a base64-encoded user-data setting is set for the host container. (The decoded contents are available to the container at /.bottlerocket/host-containers/NAME/user-data) @@ -26,13 +26,15 @@ use std::path::{Path, PathBuf}; use std::process::{self, Command}; use std::str::FromStr; -use model::modeled_types::Identifier; +use modeled_types::Identifier; const ENV_FILE_DIR: &str = "/etc/host-containers"; +const CONFIG_FILE: &str = "/etc/host-containers/host-containers.toml"; const PERSISTENT_STORAGE_BASE_DIR: &str = "/local/host-containers"; +mod config; + mod error { - use http::StatusCode; use snafu::Snafu; use std::fmt; use std::io; @@ -42,32 +44,16 @@ mod error { #[derive(Debug, Snafu)] #[snafu(visibility(pub(super)))] pub(super) enum Error { - #[snafu(display("Error sending {} to {}: {}", method, uri, source))] - APIRequest { - method: String, - uri: String, - #[snafu(source(from(apiclient::Error, Box::new)))] - source: Box, - }, - - #[snafu(display("Error {} when sending {} to {}: {}", code, method, uri, response_body))] - APIResponse { - method: String, - uri: String, - code: StatusCode, - response_body: String, + #[snafu(display("Error reading config from {}: {}", config_file, source))] + ReadConfig { + config_file: String, + source: io::Error, }, - #[snafu(display( - "Error deserializing response as JSON from {} to {}: {}", - method, - uri, - source - ))] - ResponseJson { - method: &'static str, - uri: String, - source: serde_json::Error, + #[snafu(display("Error parsing config toml from {}: {}", config_file, source))] + ConfigToml { + config_file: String, + source: toml::de::Error, }, #[snafu(display("Host containers '{}' missing field '{}'", name, field))] @@ -134,34 +120,23 @@ mod error { type Result = std::result::Result; -/// Query the API for the currently defined host containers -async fn get_host_containers

(socket_path: P) -> Result> +/// Read the currently defined host containers from the config file +fn get_host_containers

(config_path: P) -> Result> where P: AsRef, { - debug!("Querying the API for settings"); - - let method = "GET"; - let uri = constants::API_SETTINGS_URI; - let (code, response_body) = apiclient::raw_request(&socket_path, uri, method, None) - .await - .context(error::APIRequestSnafu { method, uri })?; - ensure!( - code.is_success(), - error::APIResponseSnafu { - method, - uri, - code, - response_body, - } - ); - - // Build a Settings struct from the response string - let settings: model::Settings = - serde_json::from_str(&response_body).context(error::ResponseJsonSnafu { method, uri })?; + debug!("Reading containers from the config file!"); + let config_path = config_path.as_ref(); + let config = std::fs::read_to_string(config_path).context(error::ReadConfigSnafu { + config_file: format!("{:?}", config_path), + })?; + let config: config::HostContainersConfig = + toml::from_str(&config).context(error::ConfigTomlSnafu { + config_file: format!("{:?}", config_path), + })?; // If host containers aren't defined, return an empty map - Ok(settings.host_containers.unwrap_or_default()) + Ok(config.host_containers.unwrap_or_default()) } /// SystemdUnit stores the systemd unit being manipulated @@ -288,7 +263,7 @@ where /// Store the args we receive on the command line struct Args { log_level: LevelFilter, - socket_path: PathBuf, + config_path: PathBuf, } /// Print a usage message in the event a bad arg is passed @@ -296,12 +271,11 @@ fn usage() -> ! { let program_name = env::args().next().unwrap_or_else(|| "program".to_string()); eprintln!( r"Usage: {} - [ --socket-path PATH ] + [ --config-path PATH ] [ --log-level trace|debug|info|warn|error ] - Socket path defaults to {}", - program_name, - constants::API_SOCKET, + Config path defaults to {}", + program_name, CONFIG_FILE, ); process::exit(2); } @@ -315,7 +289,7 @@ fn usage_msg>(msg: S) -> ! { /// Parse the args to the program and return an Args struct fn parse_args(args: env::Args) -> Args { let mut log_level = None; - let mut socket_path = None; + let mut config_path = None; let mut iter = args.skip(1); while let Some(arg) = iter.next() { @@ -329,10 +303,10 @@ fn parse_args(args: env::Args) -> Args { })); } - "--socket-path" => { - socket_path = Some( + "--config-path" => { + config_path = Some( iter.next() - .unwrap_or_else(|| usage_msg("Did not give argument to --socket-path")) + .unwrap_or_else(|| usage_msg("Did not give argument to --config-path")) .into(), ) } @@ -343,15 +317,15 @@ fn parse_args(args: env::Args) -> Args { Args { log_level: log_level.unwrap_or(LevelFilter::Info), - socket_path: socket_path.unwrap_or_else(|| constants::API_SOCKET.into()), + config_path: config_path.unwrap_or_else(|| CONFIG_FILE.into()), } } -fn handle_host_container(name: S, image_details: &model::HostContainer) -> Result<()> +fn handle_host_container(name: S, image_details: &config::HostContainer) -> Result<()> where S: AsRef, { - // Get basic settings, as retrieved from API. + // Get basic settings, as retrieved from the config file. let name = name.as_ref(); let source = image_details .source @@ -469,7 +443,7 @@ fn is_container_affected(settings: &[&str], container_name: &str) -> bool { false } -async fn run() -> Result<()> { +fn run() -> Result<()> { let args = parse_args(env::args()); // this env var is passed by thar-be-settings let changed_settings_env = env::var("CHANGED_SETTINGS").unwrap_or_else(|_| "".to_string()); @@ -481,7 +455,7 @@ async fn run() -> Result<()> { info!("host-containers started"); let mut failed = 0usize; - let host_containers = get_host_containers(args.socket_path).await?; + let host_containers = get_host_containers(args.config_path)?; for (name, image_details) in host_containers.iter() { // handle all host containers during startup // handle the host container that has settings changed during restart @@ -507,9 +481,8 @@ async fn run() -> Result<()> { // Returning a Result from main makes it print a Debug representation of the error, but with Snafu // we have nice Display representations of the error, so we wrap "main" (run) and print any error. // https://github.com/shepmaster/snafu/issues/110 -#[tokio::main] -async fn main() { - if let Err(e) = run().await { +fn main() { + if let Err(e) = run() { eprintln!("{}", e); process::exit(1); }