From 363489a7fb23385a85810b213755325fcabcc964 Mon Sep 17 00:00:00 2001 From: "Steve Lee (POWERSHELL HE/HIM) (from Dev Box)" Date: Wed, 30 Apr 2025 14:38:48 -0700 Subject: [PATCH 1/3] Fix adapter configuration set which returns `result`, but needs to be `resources` --- dsc_lib/src/configure/mod.rs | 14 ++++++++++---- dsc_lib/src/dscresources/command_resource.rs | 12 ++++++++++-- dsc_lib/src/dscresources/dscresource.rs | 2 +- .../Tests/powershellgroup.config.tests.ps1 | 7 ++++--- 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/dsc_lib/src/configure/mod.rs b/dsc_lib/src/configure/mod.rs index af7c9e2de..77ab92d15 100644 --- a/dsc_lib/src/configure/mod.rs +++ b/dsc_lib/src/configure/mod.rs @@ -405,12 +405,18 @@ impl Configurator { let GetResult::Resource(after_result) = after_result else { return Err(DscError::NotSupported(t!("configure.mod.groupNotSupportedForDelete").to_string())) }; - let before_value = serde_json::to_value(&before_response.actual_state)?; - let after_value = serde_json::to_value(&after_result.actual_state)?; + let diff = get_diff(&before_response.actual_state, &after_result.actual_state); + let mut before: Map = serde_json::from_value(before_response.actual_state)?; + // a `get` will return a `result` property, but an actual `set` will have that as `resources` + if before.contains_key("result") && !before.contains_key("resources") { + before.insert("resource".to_string() ,before["result"].clone()); + before.remove("result"); + } + let before_value = serde_json::to_value(&before)?; SetResult::Resource(ResourceSetResponse { - before_state: before_response.actual_state, + before_state: before_value.clone(), after_state: after_result.actual_state, - changed_properties: Some(get_diff(&before_value, &after_value)), + changed_properties: Some(diff), }) }, GetResult::Group(_) => { diff --git a/dsc_lib/src/dscresources/command_resource.rs b/dsc_lib/src/dscresources/command_resource.rs index 2ee3308be..d96c83a1e 100644 --- a/dsc_lib/src/dscresources/command_resource.rs +++ b/dsc_lib/src/dscresources/command_resource.rs @@ -5,7 +5,7 @@ use clap::ValueEnum; use jsonschema::Validator; use rust_i18n::t; use serde::Deserialize; -use serde_json::Value; +use serde_json::{Map, Value}; use std::{collections::HashMap, env, process::Stdio}; use crate::configure::{config_doc::ExecutionKind, config_result::{ResourceGetResult, ResourceTestResult}}; use crate::dscerror::DscError; @@ -146,13 +146,21 @@ pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_te verify_json(resource, cwd, &stdout)?; } - let pre_state: Value = if exit_code == 0 { + let mut pre_state_map: Map = if exit_code == 0 { serde_json::from_str(&stdout)? } else { return Err(DscError::Command(resource.resource_type.clone(), exit_code, stderr)); }; + // if the resource is an adapter, then the `get` will return a `result`, but a full `set` expects the before state to be `resources` + if resource.kind == Some(Kind::Adapter) && pre_state_map.contains_key("result") && !pre_state_map.contains_key("resources") { + pre_state_map.insert("resources".to_string(), pre_state_map["result"].clone()); + pre_state_map.remove("result"); + } + + let pre_state = serde_json::to_value(pre_state_map)?; + let mut env: Option> = None; let mut input_desired: Option<&str> = None; let args = process_args(set.args.as_ref(), desired); diff --git a/dsc_lib/src/dscresources/dscresource.rs b/dsc_lib/src/dscresources/dscresource.rs index d69d4c8f1..644dc1526 100644 --- a/dsc_lib/src/dscresources/dscresource.rs +++ b/dsc_lib/src/dscresources/dscresource.rs @@ -259,7 +259,7 @@ impl Invoke for DscResource { }; let before_state = resource_result.before_state .as_object().ok_or(DscError::Operation(t!("dscresources.dscresource.propertyIncorrectType", property = "beforeState", property_type = "object").to_string()))? - .get("result").ok_or(DscError::Operation(t!("dscresources.dscresource.propertyNotFound", property = "result").to_string()))? + .get("resources").ok_or(DscError::Operation(t!("dscresources.dscresource.propertyNotFound", property = "resources").to_string()))? .as_array().ok_or(DscError::Operation(t!("dscresources.dscresource.propertyIncorrectType", property = "result", property_type = "array").to_string()))?[0] .as_object().ok_or(DscError::Operation(t!("dscresources.dscresource.propertyIncorrectType", property = "result", property_type = "object").to_string()))? .get("properties").ok_or(DscError::Operation(t!("dscresources.dscresource.propertyNotFound", property = "properties").to_string()))?.clone(); diff --git a/powershell-adapter/Tests/powershellgroup.config.tests.ps1 b/powershell-adapter/Tests/powershellgroup.config.tests.ps1 index 9cdfdb264..649365100 100644 --- a/powershell-adapter/Tests/powershellgroup.config.tests.ps1 +++ b/powershell-adapter/Tests/powershellgroup.config.tests.ps1 @@ -221,9 +221,10 @@ Describe 'PowerShell adapter resource tests' { Name: 'DSCv3' "@ - $out = dsc config $operation -i $yaml | ConvertFrom-Json - $text = dsc config $operation -i $yaml | Out-String - $LASTEXITCODE | Should -Be 0 + $out = dsc -l trace config $operation -i $yaml 2> $TestDrive/tracing.txt + $text = $out | Out-String + $out = $out | ConvertFrom-Json + $LASTEXITCODE | Should -Be 0 -Because (Get-Content -Raw -Path $TestDrive/tracing.txt) switch ($Operation) { 'get' { $out.results[0].result.actualState.Name | Should -BeExactly 'TestClassResource1' -Because $text From 4f95da25391922714dd952064504ed276a4c07f0 Mon Sep 17 00:00:00 2001 From: "Steve Lee (POWERSHELL HE/HIM) (from Dev Box)" Date: Wed, 30 Apr 2025 15:43:37 -0700 Subject: [PATCH 2/3] handle case where group resource returns an array --- dsc_lib/src/dscresources/command_resource.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/dsc_lib/src/dscresources/command_resource.rs b/dsc_lib/src/dscresources/command_resource.rs index d96c83a1e..83474eee3 100644 --- a/dsc_lib/src/dscresources/command_resource.rs +++ b/dsc_lib/src/dscresources/command_resource.rs @@ -146,20 +146,24 @@ pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_te verify_json(resource, cwd, &stdout)?; } - let mut pre_state_map: Map = if exit_code == 0 { + let pre_state_value: Value = if exit_code == 0 { serde_json::from_str(&stdout)? } else { return Err(DscError::Command(resource.resource_type.clone(), exit_code, stderr)); }; + let pre_state = if pre_state_value.is_object() { + let mut pre_state_map: Map = serde_json::from_value(pre_state_value)?; - // if the resource is an adapter, then the `get` will return a `result`, but a full `set` expects the before state to be `resources` - if resource.kind == Some(Kind::Adapter) && pre_state_map.contains_key("result") && !pre_state_map.contains_key("resources") { - pre_state_map.insert("resources".to_string(), pre_state_map["result"].clone()); - pre_state_map.remove("result"); - } - - let pre_state = serde_json::to_value(pre_state_map)?; + // if the resource is an adapter, then the `get` will return a `result`, but a full `set` expects the before state to be `resources` + if resource.kind == Some(Kind::Adapter) && pre_state_map.contains_key("result") && !pre_state_map.contains_key("resources") { + pre_state_map.insert("resources".to_string(), pre_state_map["result"].clone()); + pre_state_map.remove("result"); + } + serde_json::to_value(pre_state_map)? + } else { + pre_state_value + }; let mut env: Option> = None; let mut input_desired: Option<&str> = None; From 43e153a3993fc8d681d9d1dcab61dcfc8df0d940 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Thu, 1 May 2025 12:04:10 -0700 Subject: [PATCH 3/3] Update dsc_lib/src/configure/mod.rs Co-authored-by: Tess Gauthier --- dsc_lib/src/configure/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dsc_lib/src/configure/mod.rs b/dsc_lib/src/configure/mod.rs index 77ab92d15..3039e581a 100644 --- a/dsc_lib/src/configure/mod.rs +++ b/dsc_lib/src/configure/mod.rs @@ -409,7 +409,7 @@ impl Configurator { let mut before: Map = serde_json::from_value(before_response.actual_state)?; // a `get` will return a `result` property, but an actual `set` will have that as `resources` if before.contains_key("result") && !before.contains_key("resources") { - before.insert("resource".to_string() ,before["result"].clone()); + before.insert("resources".to_string() ,before["result"].clone()); before.remove("result"); } let before_value = serde_json::to_value(&before)?;