diff --git a/src/agent/Cargo.lock b/src/agent/Cargo.lock index f922acba94..44a4674440 100644 --- a/src/agent/Cargo.lock +++ b/src/agent/Cargo.lock @@ -544,6 +544,7 @@ dependencies = [ "rustjail", "scan_fmt", "scopeguard", + "serde", "serde_json", "slog", "slog-scope", @@ -552,6 +553,7 @@ dependencies = [ "thiserror", "tokio", "tokio-vsock", + "toml", "tracing", "tracing-opentelemetry", "tracing-subscriber", @@ -1323,18 +1325,18 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "serde" -version = "1.0.126" +version = "1.0.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7505abeacaec74ae4778d9d9328fe5a5d04253220a85c4ee022239fc996d03" +checksum = "d1f72836d2aa753853178eda473a3b9d8e4eefdaf20523b919677e6de489f8f1" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.126" +version = "1.0.129" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "963a7dbc9895aeac7ac90e74f34a5d5261828f79df35cbed41e10189d3804d43" +checksum = "e57ae87ad533d9a56427558b516d0adac283614e347abf85b0dc0cbbf0a249f3" dependencies = [ "proc-macro2 1.0.26", "quote 1.0.9", @@ -1618,6 +1620,15 @@ dependencies = [ "vsock", ] +[[package]] +name = "toml" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31142970826733df8241ef35dc040ef98c679ab14d7c3e54d827099b3acecaa" +dependencies = [ + "serde", +] + [[package]] name = "tracing" version = "0.1.26" diff --git a/src/agent/Cargo.toml b/src/agent/Cargo.toml index 10cf2082c5..3715da42fc 100644 --- a/src/agent/Cargo.toml +++ b/src/agent/Cargo.toml @@ -58,6 +58,10 @@ tracing-opentelemetry = "0.13.0" opentelemetry = { version = "0.14.0", features = ["rt-tokio-current-thread"]} vsock-exporter = { path = "vsock-exporter" } +# Configuration +serde = { version = "1.0.129", features = ["derive"] } +toml = "0.5.8" + [dev-dependencies] tempfile = "3.1.0" diff --git a/src/agent/src/config.rs b/src/agent/src/config.rs index 960dda552c..108464a4a5 100644 --- a/src/agent/src/config.rs +++ b/src/agent/src/config.rs @@ -4,8 +4,10 @@ // use crate::tracer; use anyhow::{bail, ensure, Context, Result}; +use serde::Deserialize; use std::env; use std::fs; +use std::str::FromStr; use std::time; use tracing::instrument; @@ -19,6 +21,7 @@ const DEBUG_CONSOLE_VPORT_OPTION: &str = "agent.debug_console_vport"; const LOG_VPORT_OPTION: &str = "agent.log_vport"; const CONTAINER_PIPE_SIZE_OPTION: &str = "agent.container_pipe_size"; const UNIFIED_CGROUP_HIERARCHY_OPTION: &str = "agent.unified_cgroup_hierarchy"; +const CONFIG_FILE: &str = "agent.config_file"; const DEFAULT_LOG_LEVEL: slog::Level = slog::Level::Info; const DEFAULT_HOTPLUG_TIMEOUT: time::Duration = time::Duration::from_secs(3); @@ -47,7 +50,7 @@ const ERR_INVALID_CONTAINER_PIPE_SIZE_PARAM: &str = "unable to parse container p const ERR_INVALID_CONTAINER_PIPE_SIZE_KEY: &str = "invalid container pipe size key name"; const ERR_INVALID_CONTAINER_PIPE_NEGATIVE: &str = "container pipe size should not be negative"; -#[derive(Debug, Default)] +#[derive(Debug, Default, Deserialize)] pub struct EndpointsConfig { pub allowed: Vec, } @@ -67,6 +70,35 @@ pub struct AgentConfig { pub endpoints: EndpointsConfig, } +#[derive(Debug, Deserialize)] +pub struct AgentConfigBuilder { + pub debug_console: Option, + pub dev_mode: Option, + pub log_level: Option, + pub hotplug_timeout: Option, + pub debug_console_vport: Option, + pub log_vport: Option, + pub container_pipe_size: Option, + pub server_addr: Option, + pub unified_cgroup_hierarchy: Option, + pub tracing: Option, + pub endpoints: Option, +} + +macro_rules! config_override { + ($builder:ident, $config:ident, $field:ident) => { + if let Some(v) = $builder.$field { + $config.$field = v; + } + }; + + ($builder:ident, $config:ident, $field:ident, $func: ident) => { + if let Some(v) = $builder.$field { + $config.$field = $func(&v)?; + } + }; +} + // parse_cmdline_param parse commandline parameters. macro_rules! parse_cmdline_param { // commandline flags, without func to parse the option values @@ -115,6 +147,36 @@ impl Default for AgentConfig { } } +impl FromStr for AgentConfig { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + let agent_config_builder: AgentConfigBuilder = + toml::from_str(s).map_err(anyhow::Error::new)?; + let mut agent_config: AgentConfig = Default::default(); + + // Overwrite default values with the configuration files ones. + config_override!(agent_config_builder, agent_config, debug_console); + config_override!(agent_config_builder, agent_config, dev_mode); + config_override!( + agent_config_builder, + agent_config, + log_level, + logrus_to_slog_level + ); + config_override!(agent_config_builder, agent_config, hotplug_timeout); + config_override!(agent_config_builder, agent_config, debug_console_vport); + config_override!(agent_config_builder, agent_config, log_vport); + config_override!(agent_config_builder, agent_config, container_pipe_size); + config_override!(agent_config_builder, agent_config, server_addr); + config_override!(agent_config_builder, agent_config, unified_cgroup_hierarchy); + config_override!(agent_config_builder, agent_config, tracing); + config_override!(agent_config_builder, agent_config, endpoints); + + Ok(agent_config) + } +} + impl AgentConfig { #[instrument] pub fn from_cmdline(file: &str) -> Result { @@ -122,6 +184,15 @@ impl AgentConfig { let cmdline = fs::read_to_string(file)?; let params: Vec<&str> = cmdline.split_ascii_whitespace().collect(); for param in params.iter() { + // If we get a configuration file path from the command line, we + // generate our config from it. + // The agent will fail to start if the configuration file is not present, + // or if it can't be parsed properly. + if param.starts_with(format!("{}=", CONFIG_FILE).as_str()) { + let config_file = get_string_value(param)?; + return AgentConfig::from_config_file(&config_file); + } + // parse cmdline flags parse_cmdline_param!(param, DEBUG_CONSOLE_FLAG, config.debug_console); parse_cmdline_param!(param, DEV_MODE_FLAG, config.dev_mode); @@ -201,6 +272,12 @@ impl AgentConfig { Ok(config) } + + #[instrument] + pub fn from_config_file(file: &str) -> Result { + let config = fs::read_to_string(file)?; + AgentConfig::from_str(&config) + } } #[instrument] @@ -1262,4 +1339,29 @@ Caused by: assert_result!(d.result, result, msg); } } + + #[test] + fn test_config_builder_from_string() { + let config = AgentConfig::from_str( + r#" + dev_mode = true + server_addr = 'vsock://8:2048' + + [endpoints] + allowed = ["CreateContainer", "StartContainer"] + "#, + ) + .unwrap(); + + // Verify that the override worked + assert!(config.dev_mode); + assert_eq!(config.server_addr, "vsock://8:2048"); + assert_eq!( + config.endpoints.allowed, + vec!["CreateContainer".to_string(), "StartContainer".to_string()] + ); + + // Verify that the default values are valid + assert_eq!(config.hotplug_timeout, DEFAULT_HOTPLUG_TIMEOUT); + } } diff --git a/src/agent/src/tracer.rs b/src/agent/src/tracer.rs index 5edbf300d3..e2bf349bfb 100644 --- a/src/agent/src/tracer.rs +++ b/src/agent/src/tracer.rs @@ -7,6 +7,7 @@ use crate::config::AgentConfig; use anyhow::Result; use opentelemetry::sdk::propagation::TraceContextPropagator; use opentelemetry::{global, sdk::trace::Config, trace::TracerProvider}; +use serde::Deserialize; use slog::{info, o, Logger}; use std::collections::HashMap; use std::error::Error; @@ -17,7 +18,7 @@ use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::Registry; use ttrpc::r#async::TtrpcContext; -#[derive(Debug, PartialEq)] +#[derive(Debug, Deserialize, PartialEq)] pub enum TraceType { Disabled, Isolated,