regent_sdk/state/attribute/shell/
command.rs1use crate::error::RegentError;
2use crate::hosts::managed_host::InternalApiCallOutcome;
3use crate::hosts::managed_host::{AssessCompliance, ReachCompliance};
4use crate::hosts::properties::HostProperties;
5use crate::secrets::{SecretProvidersPool, SecretReference};
6use crate::state::Check;
7use crate::state::attribute::HostHandler;
8use crate::state::attribute::Privilege;
9use crate::state::attribute::Remediation;
10use crate::state::compliance::AttributeComplianceAssessment;
11use crate::state::expected_state::Parameter;
12use serde::{Deserialize, Serialize};
13
14#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
15#[serde(deny_unknown_fields)]
16#[serde(rename_all = "PascalCase")]
17pub struct CommandBlockExpectedState {
18 cmd: Parameter<String>,
19}
20
21impl CommandBlockExpectedState {
22 pub fn builder(cmd: &str) -> CommandBlockExpectedState {
23 CommandBlockExpectedState {
24 cmd: Parameter::Clear(cmd.to_string()),
25 }
26 }
27
28 pub fn builder_secret(sec_ref: SecretReference) -> CommandBlockExpectedState {
29 CommandBlockExpectedState {
30 cmd: Parameter::Secret(sec_ref),
31 }
32 }
33
34 pub fn build(&self) -> Result<CommandBlockExpectedState, RegentError> {
35 if let Err(details) = self.check() {
36 return Err(details);
37 }
38 Ok(self.clone())
39 }
40}
41
42impl Check for CommandBlockExpectedState {
43 fn check(&self) -> Result<(), RegentError> {
44 Ok(())
45 }
46}
47
48impl<Handler: HostHandler> AssessCompliance<Handler> for CommandBlockExpectedState {
49 async fn assess_compliance(
50 &self,
51 _host_handler: &mut Handler,
52 _host_properties: &Option<HostProperties>,
53 privilege: &Privilege,
54 _optional_secret_provider: &Option<SecretProvidersPool>,
55 ) -> Result<AttributeComplianceAssessment, RegentError> {
56 let mut remediations: Vec<Remediation> = Vec::new();
57
58 let privilege = privilege.clone();
59
60 remediations.push(Remediation::Command(CommandApiCall {
61 cmd: self.cmd.clone(),
62 privilege,
63 }));
64
65 return Ok(AttributeComplianceAssessment::NonCompliant(remediations));
66 }
67}
68
69#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
70pub struct CommandApiCall {
71 pub cmd: Parameter<String>,
72 privilege: Privilege,
73}
74
75impl CommandApiCall {
76 pub fn display(&self) -> String {
77 return format!("Run command : {}", self.cmd);
78 }
79}
80
81impl<Handler: HostHandler> ReachCompliance<Handler> for CommandApiCall {
82 async fn call(
83 &self,
84 host_handler: &mut Handler,
85 _host_properties: &Option<HostProperties>,
86 optional_secret_provider: &Option<SecretProvidersPool>,
87 ) -> Result<InternalApiCallOutcome, RegentError> {
88 let cmd_result = host_handler
89 .run_command(
90 &self
91 .cmd
92 .clone()
93 .inner_raw(optional_secret_provider)
94 .await
95 .unwrap(),
96 &self.privilege,
97 )
98 .unwrap();
99
100 if cmd_result.return_code == 0 {
101 Ok(InternalApiCallOutcome::Success(Some(cmd_result.stdout)))
102 } else {
103 Ok(InternalApiCallOutcome::Failure(format!(
104 "RC : {}, STDOUT : {}, STDERR : {}",
105 cmd_result.return_code, cmd_result.stdout, cmd_result.stderr
106 )))
107 }
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114
115 #[test]
116 fn parsing_service_module_block_from_yaml_str() {
117 let raw_attributes = "---
118- Cmd: ls -ltrh";
119
120 let _attributes: Vec<CommandBlockExpectedState> =
121 yaml_serde::from_str(raw_attributes).unwrap();
122 }
123}