Skip to content

Commit db57508

Browse files
Implement map[string]file (NR-159901) (#194)
* Implement map[string]file * Add test and fix problem on map file check * Remove double check_type * Bump nrdot version
1 parent ceb2d92 commit db57508

File tree

3 files changed

+173
-85
lines changed

3 files changed

+173
-85
lines changed

build/embedded/embedded.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ artifacts:
5656
dest: artifacts/{{.Arch}}/fluent-bit
5757

5858
- name: nr-otel-collector
59-
version: 0.4.0
59+
version: 0.5.0
6060
url: https://github.com/newrelic/opentelemetry-collector-releases/releases/download/nr-otel-collector-{{.Version | trimv}}/nr-otel-collector_{{.Version | trimv}}_linux_{{.Arch}}.deb
6161
files:
6262
- name: nt-otel-collector binary

src/config/agent_type.rs

Lines changed: 154 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -182,33 +182,45 @@ impl Agent {
182182
.values_mut()
183183
.try_for_each(|v| -> Result<(), AgentTypeError> {
184184
if let Some(TrivialValue::File(f)) = &mut v.final_value {
185-
const CONF_DIR: &str = "agentconfigs";
186-
// get current path
187-
let wd = std::env::current_dir()?;
188-
let dir = wd.join(CONF_DIR);
189-
if !dir.exists() {
190-
fs::create_dir(dir.as_path())?;
191-
}
192-
let uuid = Uuid::new_v4().to_string();
193-
let path = format!("{}/{}-config.yaml", dir.to_string_lossy(), uuid); // FIXME: PATH?
194-
let mut file = fs::OpenOptions::new()
195-
.create(true)
196-
.write(true)
197-
.open(&path)?;
198-
199-
writeln!(file, "{}", f.content)?;
200-
f.path = path;
201-
// f.path = file
202-
// .path()
203-
// .to_str()
204-
// .ok_or(AgentTypeError::InvalidFilePath)?
205-
// .to_string();
185+
write_file(f)?
186+
} else if let Some(TrivialValue::Map(m)) = &mut v.final_value {
187+
return m.iter_mut().try_for_each(|(_, mut file)| {
188+
if let TrivialValue::File(f) = &mut file {
189+
write_file(f)?;
190+
}
191+
Ok(())
192+
});
206193
}
207194
Ok(())
208195
})
209196
}
210197
}
211198

199+
fn write_file(file: &mut FilePathWithContent) -> Result<(), io::Error> {
200+
const CONF_DIR: &str = "agentconfigs";
201+
// get current path
202+
let wd = std::env::current_dir()?;
203+
let dir = wd.join(CONF_DIR);
204+
if !dir.exists() {
205+
fs::create_dir(dir.as_path())?;
206+
}
207+
let uuid = Uuid::new_v4().to_string();
208+
let path = format!("{}/{}-config.yaml", dir.to_string_lossy(), uuid); // FIXME: PATH?
209+
let mut fs_file = fs::OpenOptions::new()
210+
.create(true)
211+
.write(true)
212+
.open(&path)?;
213+
214+
writeln!(fs_file, "{}", file.content)?;
215+
file.path = path;
216+
// f.path = file
217+
// .path()
218+
// .to_str()
219+
// .ok_or(AgentTypeError::InvalidFilePath)?
220+
// .to_string();
221+
Ok(())
222+
}
223+
212224
impl TryFrom<RawAgent> for Agent {
213225
type Error = AgentTypeError;
214226
/// Convert a [`RawAgent`] into an [`Agent`].
@@ -251,6 +263,22 @@ impl TrivialValue {
251263
}
252264
Ok(self)
253265
}
266+
(TrivialValue::Map(m), VariableType::MapStringFile) => {
267+
if !m.iter().all(|(_, v)| matches!(v, TrivialValue::String(_))) {
268+
return Err(AgentTypeError::InvalidMap);
269+
}
270+
271+
Ok(TrivialValue::Map(
272+
m.into_iter()
273+
.map(|(k, v)| {
274+
(
275+
k,
276+
TrivialValue::File(FilePathWithContent::new(v.to_string())),
277+
)
278+
})
279+
.collect(),
280+
))
281+
}
254282
(TrivialValue::String(s), VariableType::File) => {
255283
Ok(TrivialValue::File(FilePathWithContent::new(s)))
256284
}
@@ -347,6 +375,8 @@ pub enum VariableType {
347375
File,
348376
#[serde(rename = "map[string]string")]
349377
MapStringString,
378+
#[serde(rename = "map[string]file")]
379+
MapStringFile,
350380
// #[serde(rename = "map[string]number")]
351381
// MapStringNumber,
352382
// #[serde(rename = "map[string]bool")]
@@ -684,9 +714,6 @@ fn normalize_agent_spec(spec: AgentVariables) -> Result<NormalizedVariables, Age
684714
if v.default.is_none() && !v.required {
685715
return Err(AgentTypeError::MissingDefaultWithKey(k.clone()));
686716
}
687-
if let Some(default) = v.default.clone() {
688-
default.check_type(v.type_)?;
689-
}
690717
Ok(())
691718
})?;
692719
Ok(r.into_iter().chain(n_spec).collect())
@@ -949,6 +976,13 @@ variables:
949976
description: "Newrelic infra configuration yaml"
950977
type: map[string]string
951978
required: true
979+
integrations:
980+
description: "Newrelic integrations configuration yamls"
981+
type: map[string]file
982+
required: true
983+
default:
984+
kafka: |
985+
bootstrap: zookeeper
952986
deployment:
953987
on_host:
954988
executables:
@@ -961,6 +995,11 @@ deployment:
961995
config3:
962996
log_level: trace
963997
forward: "true"
998+
integrations:
999+
kafka: |
1000+
strategy: bootstrap
1001+
redis: |
1002+
user: redis
9641003
config: |
9651004
license_key: abc123
9661005
staging: true
@@ -983,4 +1022,95 @@ config: |
9831022

9841023
println!("Output: {:#?}", actual);
9851024
}
1025+
1026+
// Obsolete test
1027+
1028+
/*const EXAMPLE_AGENT_YAML_REPLACE_WITH_DEFAULT: &str = r#"
1029+
name: nrdot
1030+
namespace: newrelic
1031+
version: 0.1.0
1032+
variables:
1033+
config:
1034+
description: "Path to the agent"
1035+
type: file
1036+
required: false
1037+
default: "test"
1038+
deployment:
1039+
on_host:
1040+
path:
1041+
description: "Path to the agent"
1042+
type: string
1043+
required: false
1044+
default: "/default_path"
1045+
args:
1046+
description: "Args passed to the agent"
1047+
type: string
1048+
required: false
1049+
default: "--verbose true"
1050+
integrations:
1051+
description: "Newrelic integrations configuration yamls"
1052+
type: map[string]file
1053+
required: false
1054+
default:
1055+
kafka: |
1056+
bootstrap: zookeeper
1057+
deployment:
1058+
on_host:
1059+
executables:
1060+
- path: ${deployment.on_host.args}/otelcol
1061+
args: "-c ${deployment.on_host.args}"
1062+
env: ""
1063+
"#;
1064+
1065+
#[test]
1066+
fn test_validate_with_default() {
1067+
let input_structure =
1068+
serde_yaml::from_str::<SupervisorConfig>("").unwrap();
1069+
let agent_type =
1070+
serde_yaml::from_str::<Agent>(EXAMPLE_AGENT_YAML_REPLACE_WITH_DEFAULT).unwrap();
1071+
1072+
let expected = Map::from([
1073+
(
1074+
"deployment.on_host.args".to_string(),
1075+
TrivialValue::String("--verbose true".to_string()),
1076+
),
1077+
(
1078+
"deployment.on_host.path".to_string(),
1079+
TrivialValue::String("/default_path".to_string()),
1080+
),
1081+
(
1082+
"config".to_string(),
1083+
TrivialValue::File(FilePathWithContent::new("test".to_string())),
1084+
),
1085+
(
1086+
"integrations".to_string(),
1087+
TrivialValue::Map(Map::from([(
1088+
"kafka".to_string(),
1089+
TrivialValue::File(FilePathWithContent::new(
1090+
"bootstrap: zookeeper\n".to_string(),
1091+
)),
1092+
)])),
1093+
),
1094+
]);
1095+
let actual = agent_type
1096+
.populate(input_structure)
1097+
.expect("Failed to populate the AgentType's runtime_config field");
1098+
1099+
expected.iter().for_each(|(key, expected_value)|{
1100+
let actual_value = actual.clone().get_variables(key.to_string()).unwrap().final_value;
1101+
if let Some(TrivialValue::File(actual_file)) = actual_value {
1102+
let TrivialValue::File(expected_file) = expected_value else { unreachable!() };
1103+
assert_eq!(expected_file.content, actual_file.content);
1104+
} else if let Some(TrivialValue::Map(actual_map)) = actual_value {
1105+
actual_map.iter().for_each(|(a,actual_map)|{
1106+
let TrivialValue::File(actual_file) = actual_map else { unreachable!() };
1107+
let TrivialValue::Map(expected_map) = expected_value else { unreachable!() };
1108+
let TrivialValue::File(expected_file) = expected_map.get(a).unwrap() else { unreachable!() };
1109+
assert_eq!(expected_file.content, actual_file.content);
1110+
});
1111+
} else {
1112+
assert_eq!(*expected_value, actual_value.unwrap())
1113+
}
1114+
});
1115+
}*/
9861116
}

src/config/supervisor_config.rs

Lines changed: 18 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,9 @@ deployment:
238238
path: "/etc"
239239
args: --verbose true
240240
config: test
241+
integrations:
242+
kafka: |
243+
strategy: bootstrap
241244
"#;
242245
const EXAMPLE_AGENT_YAML_REPLACE: &str = r#"
243246
name: nrdot
@@ -258,6 +261,10 @@ variables:
258261
description: "Args passed to the agent"
259262
type: string
260263
required: true
264+
integrations:
265+
description: "Newrelic integrations configuration yamls"
266+
type: map[string]file
267+
required: true
261268
deployment:
262269
on_host:
263270
executables:
@@ -290,6 +297,15 @@ deployment:
290297
"config".to_string(),
291298
TrivialValue::File(FilePathWithContent::new("test".to_string())),
292299
),
300+
(
301+
"integrations".to_string(),
302+
TrivialValue::Map(Map::from([(
303+
"kafka".to_string(),
304+
TrivialValue::File(FilePathWithContent::new(
305+
"strategy: bootstrap\n".to_string(),
306+
)),
307+
)])),
308+
),
293309
]);
294310
let actual = input_structure
295311
.normalize_with_agent_type(&agent_type)
@@ -299,10 +315,11 @@ deployment:
299315
}
300316

301317
const EXAMPLE_CONFIG_REPLACE_NOPATH: &str = r#"
302-
config: test
303318
deployment:
304319
on_host:
305320
args: --verbose true
321+
integrations: {}
322+
config: test
306323
"#;
307324

308325
#[test]
@@ -319,63 +336,4 @@ deployment:
319336
"Missing required key in config: `deployment.on_host.path`"
320337
);
321338
}
322-
323-
const EXAMPLE_AGENT_YAML_REPLACE_WITH_DEFAULT: &str = r#"
324-
name: nrdot
325-
namespace: newrelic
326-
version: 0.1.0
327-
variables:
328-
config:
329-
description: "Path to the agent"
330-
type: file
331-
required: true
332-
default: "test"
333-
deployment:
334-
on_host:
335-
path:
336-
description: "Path to the agent"
337-
type: string
338-
required: false
339-
default: "/default_path"
340-
args:
341-
description: "Args passed to the agent"
342-
type: string
343-
required: true
344-
deployment:
345-
on_host:
346-
executables:
347-
- path: ${deployment.on_host.args}/otelcol
348-
args: "-c ${deployment.on_host.args}"
349-
env: ""
350-
"#;
351-
352-
#[test]
353-
fn test_validate_with_default() {
354-
let input_structure =
355-
serde_yaml::from_str::<SupervisorConfig>(EXAMPLE_CONFIG_REPLACE_NOPATH).unwrap();
356-
let agent_type =
357-
serde_yaml::from_str::<Agent>(EXAMPLE_AGENT_YAML_REPLACE_WITH_DEFAULT).unwrap();
358-
359-
let expected = Map::from([
360-
(
361-
"deployment".to_string(),
362-
TrivialValue::Map(Map::from([(
363-
"on_host".to_string(),
364-
TrivialValue::Map(Map::from([(
365-
"args".to_string(),
366-
TrivialValue::String("--verbose true".to_string()),
367-
)])),
368-
)])),
369-
),
370-
(
371-
"config".to_string(),
372-
TrivialValue::File(FilePathWithContent::new("test".to_string())),
373-
),
374-
]);
375-
let actual = input_structure
376-
.normalize_with_agent_type(&agent_type)
377-
.unwrap();
378-
379-
assert_eq!(expected, actual.0);
380-
}
381339
}

0 commit comments

Comments
 (0)