diff --git a/src/agent/src/config.rs b/src/agent/src/config.rs index 99822e459f..dd14e533e8 100644 --- a/src/agent/src/config.rs +++ b/src/agent/src/config.rs @@ -10,6 +10,7 @@ use std::time; const DEBUG_CONSOLE_FLAG: &str = "agent.debug_console"; const DEV_MODE_FLAG: &str = "agent.devmode"; const LOG_LEVEL_OPTION: &str = "agent.log"; +const SERVER_ADDR_OPTION: &str = "agent.server_addr"; const HOTPLUG_TIMOUT_OPTION: &str = "agent.hotplug_timeout"; const DEBUG_CONSOLE_VPORT_OPTION: &str = "agent.debug_console_vport"; const LOG_VPORT_OPTION: &str = "agent.log_vport"; @@ -94,6 +95,12 @@ impl AgentConfig { // parse cmdline options parse_cmdline_param!(param, LOG_LEVEL_OPTION, self.log_level, get_log_level); + parse_cmdline_param!( + param, + SERVER_ADDR_OPTION, + self.server_addr, + get_string_value + ); // ensure the timeout is a positive value parse_cmdline_param!( @@ -234,6 +241,34 @@ fn get_bool_value(param: &str) -> Result { }) } +// Return the value from a "name=value" string. +// +// Note: +// +// - A name *and* a value is required. +// - A value can contain any number of equal signs. +// - We could/should maybe check if the name is pure whitespace +// since this is considered to be invalid. +fn get_string_value(param: &str) -> Result { + let fields: Vec<&str> = param.split('=').collect(); + + if fields.len() < 2 { + return Err(anyhow!("expected name=value")); + } + + // We need name (but the value can be blank) + if fields[0] == "" { + return Err(anyhow!("name=value parameter missing name")); + } + + let value = fields[1..].join("="); + if value == "" { + return Err(anyhow!("name=value parameter missing value")); + } + + Ok(value) +} + fn get_container_pipe_size(param: &str) -> Result { let fields: Vec<&str> = param.split('=').collect(); @@ -270,6 +305,9 @@ mod tests { const ERR_INVALID_LOG_LEVEL: &str = "invalid log level"; const ERR_INVALID_LOG_LEVEL_PARAM: &str = "invalid log level parameter"; + const ERR_INVALID_GET_VALUE_PARAM: &str = "expected name=value"; + const ERR_INVALID_GET_VALUE_NO_NAME: &str = "name=value parameter missing name"; + const ERR_INVALID_GET_VALUE_NO_VALUE: &str = "name=value parameter missing value"; const ERR_INVALID_LOG_LEVEL_KEY: &str = "invalid log level key name"; const ERR_INVALID_HOTPLUG_TIMEOUT: &str = "invalid hotplug timeout parameter"; @@ -809,6 +847,61 @@ mod tests { server_addr: TEST_SERVER_ADDR, unified_cgroup_hierarchy: false, }, + TestData { + contents: "server_addr=unix:///tmp/foo.socket", + env_vars: Vec::new(), + debug_console: false, + dev_mode: false, + log_level: DEFAULT_LOG_LEVEL, + hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT, + container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE, + server_addr: TEST_SERVER_ADDR, + unified_cgroup_hierarchy: false, + }, + TestData { + contents: "agent.server_address=unix:///tmp/foo.socket", + env_vars: Vec::new(), + debug_console: false, + dev_mode: false, + log_level: DEFAULT_LOG_LEVEL, + hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT, + container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE, + server_addr: TEST_SERVER_ADDR, + unified_cgroup_hierarchy: false, + }, + TestData { + contents: "agent.server_addr=unix:///tmp/foo.socket", + env_vars: Vec::new(), + debug_console: false, + dev_mode: false, + log_level: DEFAULT_LOG_LEVEL, + hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT, + container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE, + server_addr: "unix:///tmp/foo.socket", + unified_cgroup_hierarchy: false, + }, + TestData { + contents: " agent.server_addr=unix:///tmp/foo.socket", + env_vars: Vec::new(), + debug_console: false, + dev_mode: false, + log_level: DEFAULT_LOG_LEVEL, + hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT, + container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE, + server_addr: "unix:///tmp/foo.socket", + unified_cgroup_hierarchy: false, + }, + TestData { + contents: " agent.server_addr=unix:///tmp/foo.socket a", + env_vars: Vec::new(), + debug_console: false, + dev_mode: false, + log_level: DEFAULT_LOG_LEVEL, + hotplug_timeout: DEFAULT_HOTPLUG_TIMEOUT, + container_pipe_size: DEFAULT_CONTAINER_PIPE_SIZE, + server_addr: "unix:///tmp/foo.socket", + unified_cgroup_hierarchy: false, + }, ]; let dir = tempdir().expect("failed to create tmpdir"); @@ -1195,4 +1288,82 @@ mod tests { assert_result!(d.result, result, msg); } } + + #[test] + fn test_get_string_value() { + #[derive(Debug)] + struct TestData<'a> { + param: &'a str, + result: Result, + } + + let tests = &[ + TestData { + param: "", + result: Err(make_err(ERR_INVALID_GET_VALUE_PARAM)), + }, + TestData { + param: "=", + result: Err(make_err(ERR_INVALID_GET_VALUE_NO_NAME)), + }, + TestData { + param: "==", + result: Err(make_err(ERR_INVALID_GET_VALUE_NO_NAME)), + }, + TestData { + param: "x=", + result: Err(make_err(ERR_INVALID_GET_VALUE_NO_VALUE)), + }, + TestData { + param: "x==", + result: Ok("=".into()), + }, + TestData { + param: "x===", + result: Ok("==".into()), + }, + TestData { + param: "x==x", + result: Ok("=x".into()), + }, + TestData { + param: "x=x", + result: Ok("x".into()), + }, + TestData { + param: "x=x=", + result: Ok("x=".into()), + }, + TestData { + param: "x=x=x", + result: Ok("x=x".into()), + }, + TestData { + param: "foo=bar", + result: Ok("bar".into()), + }, + TestData { + param: "x= =", + result: Ok(" =".into()), + }, + TestData { + param: "x= =", + result: Ok(" =".into()), + }, + TestData { + param: "x= = ", + result: Ok(" = ".into()), + }, + ]; + + for (i, d) in tests.iter().enumerate() { + let msg = format!("test[{}]: {:?}", i, d); + + let result = get_string_value(d.param); + + let msg = format!("{}: result: {:?}", msg, result); + + assert_result!(d.result, result, msg); + } + } }