Skip to content

Commit 5bab14f

Browse files
committed
implement SetResult from TestResult
1 parent c8ba5f3 commit 5bab14f

File tree

6 files changed

+77
-144
lines changed

6 files changed

+77
-144
lines changed

dsc/src/resource_command.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,8 +191,8 @@ pub fn delete(dsc: &DscManager, resource_type: &str, mut input: String) {
191191
};
192192
}
193193

194-
match resource.delete(input.as_str(), &ExecutionKind::Actual) {
195-
Ok(_) => {}
194+
match resource.delete(input.as_str()) {
195+
Ok(()) => {}
196196
Err(err) => {
197197
error!("Error: {err}");
198198
exit(EXIT_DSC_ERROR);

dsc_lib/src/configure/config_result.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,17 @@ pub struct ResourceSetResult {
9999
pub result: SetResult,
100100
}
101101

102+
impl From<ResourceTestResult> for ResourceSetResult {
103+
fn from(test_result: ResourceTestResult) -> Self {
104+
Self {
105+
metadata: None,
106+
name: test_result.name,
107+
resource_type: test_result.resource_type,
108+
result: test_result.result.into(),
109+
}
110+
}
111+
}
112+
102113
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
103114
#[serde(deny_unknown_fields)]
104115
pub struct GroupResourceSetResult {

dsc_lib/src/configure/mod.rs

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4-
use crate::configure::config_doc::Metadata;
4+
use crate::configure::config_doc::{ExecutionKind, Metadata};
55
use crate::configure::parameters::Input;
66
use crate::dscerror::DscError;
77
use crate::dscresources::dscresource::get_diff;
@@ -323,30 +323,31 @@ impl Configurator {
323323
set_result = dsc_resource.set(&desired, skip_test, &self.context.execution_type)?;
324324
end_datetime = chrono::Local::now();
325325
} else if dsc_resource.capabilities.contains(&Capability::Delete) {
326+
if self.context.execution_type == ExecutionKind::WhatIf {
327+
return Err(DscError::NotSupported("What-if execution not supported for delete".to_string()));
328+
}
326329
debug!("Resource implements delete and _exist is false");
327330
let before_result = dsc_resource.get(&desired)?;
328331
start_datetime = chrono::Local::now();
329-
let delete_result = dsc_resource.delete(&desired, &self.context.execution_type)?;
330-
set_result = if let Some(whatif_result) = delete_result { whatif_result } else {
331-
let after_result = dsc_resource.get(&desired)?;
332-
// convert get result to set result
333-
match before_result {
334-
GetResult::Resource(before_response) => {
335-
let GetResult::Resource(after_result) = after_result else {
336-
return Err(DscError::NotSupported("Group resources not supported for delete".to_string()))
337-
};
338-
let before_value = serde_json::to_value(&before_response.actual_state)?;
339-
let after_value = serde_json::to_value(&after_result.actual_state)?;
340-
SetResult::Resource(ResourceSetResponse {
341-
before_state: before_response.actual_state,
342-
after_state: after_result.actual_state,
343-
changed_properties: Some(get_diff(&before_value, &after_value)),
344-
})
345-
},
346-
GetResult::Group(_) => {
347-
return Err(DscError::NotSupported("Group resources not supported for delete".to_string()));
348-
},
349-
}
332+
dsc_resource.delete(&desired)?;
333+
let after_result = dsc_resource.get(&desired)?;
334+
// convert get result to set result
335+
set_result = match before_result {
336+
GetResult::Resource(before_response) => {
337+
let GetResult::Resource(after_result) = after_result else {
338+
return Err(DscError::NotSupported("Group resources not supported for delete".to_string()))
339+
};
340+
let before_value = serde_json::to_value(&before_response.actual_state)?;
341+
let after_value = serde_json::to_value(&after_result.actual_state)?;
342+
SetResult::Resource(ResourceSetResponse {
343+
before_state: before_response.actual_state,
344+
after_state: after_result.actual_state,
345+
changed_properties: Some(get_diff(&before_value, &after_value)),
346+
})
347+
},
348+
GetResult::Group(_) => {
349+
return Err(DscError::NotSupported("Group resources not supported for delete".to_string()));
350+
},
350351
};
351352
end_datetime = chrono::Local::now();
352353
} else {

dsc_lib/src/dscresources/command_resource.rs

Lines changed: 16 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
use jsonschema::JSONSchema;
55
use serde_json::Value;
66
use std::{collections::HashMap, env, io::{Read, Write}, process::{Command, Stdio}};
7-
use crate::{dscerror::DscError, dscresources::invoke_result::{ResourceGetResponse, ResourceSetResponse, ResourceSetWhatIfResponse, ResourceTestResponse, WhatIfResult}};
7+
use crate::{dscerror::DscError, dscresources::invoke_result::{ResourceGetResponse, ResourceSetResponse, ResourceTestResponse}};
88
use crate::configure::{config_doc::ExecutionKind, config_result::ResourceGetResult};
9-
use super::{dscresource::{get_diff, get_diff_what_if}, invoke_result::{ExportResult, GetResult, SetResult, TestResult, ValidateResult}, resource_manifest::{ArgKind, InputKind, Kind, ResourceManifest, ReturnKind, SchemaKind}};
9+
use super::{dscresource::get_diff, invoke_result::{ExportResult, GetResult, SetResult, TestResult, ValidateResult}, resource_manifest::{ArgKind, InputKind, Kind, ResourceManifest, ReturnKind, SchemaKind}};
1010
use tracing::{error, warn, info, debug, trace};
1111

1212
pub const EXIT_PROCESS_TERMINATED: i32 = 0x102;
@@ -99,45 +99,35 @@ pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_te
9999
// if resource doesn't implement a pre-test, we execute test first to see if a set is needed
100100
if !skip_test && set.pre_test != Some(true) {
101101
info!("No pretest, invoking test {}", &resource.resource_type);
102-
let (in_desired_state, actual_state, desired_state) = match invoke_test(resource, cwd, desired)? {
102+
let test_result = invoke_test(resource, cwd, desired)?;
103+
if execution_type == &ExecutionKind::WhatIf {
104+
return Ok(test_result.into());
105+
}
106+
let (in_desired_state, actual_state) = match test_result {
103107
TestResult::Group(group_response) => {
104108
let mut result_array: Vec<Value> = Vec::new();
105109
for result in group_response.results {
106110
result_array.push(serde_json::to_value(result)?);
107111
}
108-
(group_response.in_desired_state, Value::from(result_array), Value::String("not implemented".to_string()))
112+
(group_response.in_desired_state, Value::from(result_array))
109113
},
110114
TestResult::Resource(response) => {
111-
(response.in_desired_state, response.actual_state, response.desired_state)
115+
(response.in_desired_state, response.actual_state)
112116
}
113117
};
114118

115-
if execution_type == &ExecutionKind::WhatIf {
116-
if desired_state == Value::String("not implemented".to_string()) {
117-
return Err(DscError::NotImplemented(format!("Group '{}' does not currently support `what-if flag`", resource.resource_type)));
118-
}
119-
if in_desired_state {
120-
debug!("what-if: resource is already in desired state, returning null ResourceWhatIf response");
121-
return Ok(SetResult::ResourceWhatIf(ResourceSetWhatIfResponse{
122-
what_if_changes: WhatIfResult::Diff(Vec::new())
123-
}));
124-
}
125-
debug!("what-if: resource is not in desired state, returning diff ResourceWhatIf response");
126-
let diff_properties = get_diff_what_if( &desired_state, &actual_state);
127-
return Ok(SetResult::ResourceWhatIf(ResourceSetWhatIfResponse{
128-
what_if_changes: WhatIfResult::Diff(diff_properties)
129-
}));
130-
}
131119
if in_desired_state {
132120
return Ok(SetResult::Resource(ResourceSetResponse{
133121
before_state: serde_json::from_str(desired)?,
134122
after_state: actual_state,
135123
changed_properties: None,
136124
}));
137125
}
138-
} else if execution_type == &ExecutionKind::WhatIf {
139-
// until resources implement what-if, if we are in what-if mode and there is no pre-test, we can't determine what-if changes
140-
return Err(DscError::NotImplemented(format!("Resource '{}' does not currently support `what-if flag` because there is no pre-test", resource.resource_type)));
126+
}
127+
128+
if ExecutionKind::WhatIf == *execution_type {
129+
// until resources implement their own what-if, return an error here
130+
return Err(DscError::NotImplemented("what-if not yet supported for resources that implement pre-test".to_string()));
141131
}
142132

143133
let args = process_args(&resource.get.args, desired);
@@ -368,7 +358,7 @@ fn invoke_synthetic_test(resource: &ResourceManifest, cwd: &str, expected: &str)
368358
/// # Errors
369359
///
370360
/// Error is returned if the underlying command returns a non-zero exit code.
371-
pub fn invoke_delete(resource: &ResourceManifest, cwd: &str, filter: &str, execution_type: &ExecutionKind) -> Result<Option<SetResult>, DscError> {
361+
pub fn invoke_delete(resource: &ResourceManifest, cwd: &str, filter: &str) -> Result<(), DscError> {
372362
let Some(delete) = &resource.delete else {
373363
return Err(DscError::NotImplemented("delete".to_string()));
374364
};
@@ -378,15 +368,10 @@ pub fn invoke_delete(resource: &ResourceManifest, cwd: &str, filter: &str, execu
378368
let args = process_args(&delete.args, filter);
379369
let command_input = get_command_input(&delete.input, filter)?;
380370

381-
if execution_type == &ExecutionKind::WhatIf {
382-
return Ok(Some(SetResult::ResourceWhatIf(ResourceSetWhatIfResponse{
383-
what_if_changes: WhatIfResult::String(format!("delete '{}' using '{}'", &resource.resource_type, &delete.executable))
384-
})));
385-
}
386371
info!("Invoking delete '{}' using '{}'", &resource.resource_type, &delete.executable);
387372
let (_exit_code, _stdout, _stderr) = invoke_command(&delete.executable, args, command_input.stdin.as_deref(), Some(cwd), command_input.env)?;
388373

389-
Ok(None)
374+
Ok(())
390375
}
391376

392377
/// Invoke the validate operation against a command resource.

dsc_lib/src/dscresources/dscresource.rs

Lines changed: 4 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
88
use serde_json::Value;
99
use std::collections::HashMap;
1010

11-
use super::{command_resource, dscerror, invoke_result::{ExportResult, GetResult, ResourceTestResponse, SetResult, TestResult, ValidateResult, WhatIfChanges}, resource_manifest::import_manifest};
11+
use super::{command_resource, dscerror, invoke_result::{ExportResult, GetResult, ResourceTestResponse, SetResult, TestResult, ValidateResult}, resource_manifest::import_manifest};
1212

1313
#[derive(Clone, Debug, Deserialize, Serialize, JsonSchema)]
1414
#[serde(deny_unknown_fields)]
@@ -138,7 +138,7 @@ pub trait Invoke {
138138
/// # Errors
139139
///
140140
/// This function will return an error if the underlying resource fails.
141-
fn delete(&self, filter: &str, execution_type: &ExecutionKind) -> Result<Option<SetResult>, DscError>;
141+
fn delete(&self, filter: &str) -> Result<(), DscError>;
142142

143143
/// Invoke the validate operation on the resource.
144144
///
@@ -244,7 +244,7 @@ impl Invoke for DscResource {
244244
}
245245
}
246246

247-
fn delete(&self, filter: &str, execution_type: &ExecutionKind) -> Result<Option<SetResult>, DscError> {
247+
fn delete(&self, filter: &str) -> Result<(), DscError> {
248248
match &self.implemented_as {
249249
ImplementedAs::Custom(_custom) => {
250250
Err(DscError::NotImplemented("set custom resources".to_string()))
@@ -254,7 +254,7 @@ impl Invoke for DscResource {
254254
return Err(DscError::MissingManifest(self.type_name.clone()));
255255
};
256256
let resource_manifest = import_manifest(manifest.clone())?;
257-
command_resource::invoke_delete(&resource_manifest, &self.directory, filter, execution_type)
257+
command_resource::invoke_delete(&resource_manifest, &self.directory, filter)
258258
},
259259
}
260260
}
@@ -356,65 +356,3 @@ pub fn get_diff(expected: &Value, actual: &Value) -> Vec<String> {
356356

357357
diff_properties
358358
}
359-
360-
#[must_use]
361-
pub fn get_diff_what_if(expected: &Value, actual: &Value) -> Vec<WhatIfChanges> {
362-
let mut diff_properties: Vec<WhatIfChanges> = Vec::new();
363-
if expected.is_null() {
364-
return diff_properties;
365-
}
366-
367-
let mut expected = expected.clone();
368-
let mut actual = actual.clone();
369-
370-
if let Some(map) = expected.as_object_mut() {
371-
// handle well-known optional properties with default values by adding them
372-
for (key, value) in get_well_known_properties() {
373-
if !map.contains_key(&key) {
374-
map.insert(key.clone(), value.clone());
375-
}
376-
377-
if actual.is_object() && actual[&key].is_null() {
378-
actual[key.clone()] = value.clone();
379-
}
380-
}
381-
382-
for (key, value) in &*map {
383-
let mut is_diff = false;
384-
if value.is_object() {
385-
let sub_diff = get_diff_what_if(value, &actual[key]);
386-
if !sub_diff.is_empty() {
387-
is_diff = true;
388-
}
389-
}
390-
else {
391-
match actual.as_object() {
392-
Some(actual_object) => {
393-
if actual_object.contains_key(key) {
394-
if value != &actual[key] {
395-
is_diff = true;
396-
}
397-
}
398-
else {
399-
is_diff = true;
400-
}
401-
},
402-
None => {
403-
is_diff = true;
404-
},
405-
}
406-
}
407-
if is_diff {
408-
diff_properties.push(
409-
WhatIfChanges {
410-
name: key.to_string(),
411-
from: actual[key].clone(),
412-
to: value.clone(),
413-
}
414-
);
415-
}
416-
}
417-
}
418-
419-
diff_properties
420-
}

dsc_lib/src/dscresources/invoke_result.rs

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -66,10 +66,30 @@ impl Default for GroupResourceSetResponse {
6666
#[serde(untagged)]
6767
pub enum SetResult {
6868
Resource(ResourceSetResponse),
69-
ResourceWhatIf(ResourceSetWhatIfResponse),
7069
Group(GroupResourceSetResponse),
7170
}
7271

72+
impl From<TestResult> for SetResult {
73+
fn from(value: TestResult) -> Self {
74+
match value {
75+
TestResult::Group(group) => {
76+
let mut results = Vec::<ResourceSetResult>::new();
77+
for result in group.results {
78+
results.push(result.into());
79+
}
80+
SetResult::Group(GroupResourceSetResponse { results })
81+
},
82+
TestResult::Resource(resource) => {
83+
SetResult::Resource(ResourceSetResponse {
84+
before_state: resource.actual_state,
85+
after_state: resource.desired_state,
86+
changed_properties: if resource.diff_properties.is_empty() { None } else { Some(resource.diff_properties) },
87+
})
88+
}
89+
}
90+
}
91+
}
92+
7393
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
7494
#[serde(deny_unknown_fields)]
7595
pub struct ResourceSetResponse {
@@ -84,28 +104,6 @@ pub struct ResourceSetResponse {
84104
pub changed_properties: Option<Vec<String>>,
85105
}
86106

87-
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
88-
#[serde(deny_unknown_fields)]
89-
pub struct ResourceSetWhatIfResponse {
90-
#[serde(rename = "changes")]
91-
pub what_if_changes: WhatIfResult
92-
}
93-
94-
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
95-
#[serde(untagged)]
96-
pub enum WhatIfResult {
97-
Diff(Vec<WhatIfChanges>),
98-
String(String)
99-
}
100-
101-
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
102-
#[serde(deny_unknown_fields)]
103-
pub struct WhatIfChanges {
104-
pub name: String,
105-
pub from: Value,
106-
pub to: Value
107-
}
108-
109107
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
110108
#[serde(deny_unknown_fields)]
111109
pub struct GroupResourceTestResponse {

0 commit comments

Comments
 (0)