Skip to main content

regent_sdk/state/attribute/shell/
command.rs

1use 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}