elasticctl_api/fleet/agent_policy_ops/
artifact.rs1use super::{
4 AgentPolicySpec, AgentPolicySummary, BTreeMap, BTreeSet, ContentFormat, Error, ErrorKind,
5 ExportOutcome, Path, Result, Transport, collect, content_codec, is_platform_owned,
6 live_from_item, read_live, resolve_item,
7};
8
9pub fn validate(path: &Path) -> Result<Vec<AgentPolicySpec>> {
11 let body = std::fs::read_to_string(path).map_err(|error| {
12 Error::new(
13 ErrorKind::Error,
14 format!("reading {}: {error}", path.display()),
15 )
16 })?;
17 let mut specs = content_codec::decode_sequence::<AgentPolicySpec>(
18 &body,
19 ContentFormat::from_path(path),
20 "agent policy",
21 )?;
22 let mut seen_ids = BTreeSet::new();
23 let mut duplicate_ids = BTreeSet::new();
24 let mut seen_names = BTreeSet::new();
25 let mut duplicate_names = BTreeSet::new();
26 for spec in &specs {
27 if !seen_ids.insert(spec.id.as_str()) {
28 duplicate_ids.insert(spec.id.as_str());
29 }
30 if !seen_names.insert(spec.name.as_str()) {
31 duplicate_names.insert(spec.name.as_str());
32 }
33 }
34 if !duplicate_ids.is_empty() {
35 return Err(Error::new(
36 ErrorKind::Error,
37 format!(
38 "duplicate agent policy ids: {}",
39 duplicate_ids.into_iter().collect::<Vec<_>>().join(", ")
40 ),
41 ));
42 }
43 if !duplicate_names.is_empty() {
44 return Err(Error::new(
45 ErrorKind::Error,
46 format!(
47 "duplicate agent policy names: {}",
48 duplicate_names.into_iter().collect::<Vec<_>>().join(", ")
49 ),
50 ));
51 }
52 specs.sort_by(|left, right| left.id.cmp(&right.id));
53 Ok(specs)
54}
55
56pub async fn export(
58 transport: &Transport,
59 selectors: &[String],
60 all_custom: bool,
61 format: ContentFormat,
62) -> Result<ExportOutcome> {
63 if selectors.is_empty() && !all_custom {
64 return Err(Error::new(
65 ErrorKind::Error,
66 "agent-policy export needs selectors or --all-custom",
67 ));
68 }
69 if !selectors.is_empty() && all_custom {
70 return Err(Error::new(
71 ErrorKind::Error,
72 "--all-custom cannot be combined with selectors",
73 ));
74 }
75 let mut resolved_items = BTreeMap::new();
76 let ids: BTreeSet<String> = if all_custom {
77 let mut ids = BTreeSet::new();
78 for item in collect(transport).await? {
79 if !is_platform_owned(&item)? {
80 ids.insert(AgentPolicySummary::from_item(&item)?.id);
81 }
82 }
83 ids
84 } else {
85 let mut ids = BTreeSet::new();
86 for selector in selectors {
87 let resolved = resolve_item(transport, selector).await?;
88 ids.insert(resolved.summary.id.clone());
89 resolved_items.insert(resolved.summary.id, resolved.item);
90 }
91 ids
92 };
93 let mut specs = Vec::with_capacity(ids.len());
94 for id in &ids {
95 let live = match resolved_items.get(id) {
96 Some(item) => live_from_item(item, id, transport.space())?,
97 None => read_live(transport, id).await?,
98 };
99 specs.push(live.spec);
100 }
101 specs.sort_by(|left, right| left.id.cmp(&right.id));
102 let body = content_codec::encode_sequence(&specs, format)?;
103 Ok(ExportOutcome {
104 body,
105 exported: specs.len() as u64,
106 missing: Vec::new(),
107 })
108}