From 246215c49961c28e80073cb7675bd13484560c4d Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Thu, 19 Mar 2026 14:58:34 -0700 Subject: [PATCH 1/2] Fix executionInformation in config export result --- dsc/src/resource_command.rs | 5 ++-- dsc/tests/dsc_config_export.tests.ps1 | 34 +++++++++++++++++++++++++ lib/dsc-lib/src/configure/config_doc.rs | 6 ++--- lib/dsc-lib/src/configure/mod.rs | 12 ++++++--- 4 files changed, 49 insertions(+), 8 deletions(-) create mode 100644 dsc/tests/dsc_config_export.tests.ps1 diff --git a/dsc/src/resource_command.rs b/dsc/src/resource_command.rs index cc4c0216a..570e81ec4 100644 --- a/dsc/src/resource_command.rs +++ b/dsc/src/resource_command.rs @@ -3,7 +3,7 @@ use crate::args::{GetOutputFormat, OutputFormat}; use crate::util::{EXIT_DSC_ERROR, EXIT_INVALID_ARGS, EXIT_JSON_ERROR, EXIT_DSC_RESOURCE_NOT_FOUND, write_object}; -use dsc_lib::configure::config_doc::{Configuration, ExecutionKind}; +use dsc_lib::configure::config_doc::{Configuration, ExecutionInformation, ExecutionKind}; use dsc_lib::configure::add_resource_export_results_to_configuration; use dsc_lib::discovery::discovery_trait::DiscoveryFilter; use dsc_lib::dscresources::{resource_manifest::Kind, invoke_result::{DeleteResultKind, GetResult, ResourceGetResponse, ResourceSetResponse, SetResult}}; @@ -339,7 +339,8 @@ pub fn export(dsc: &mut DscManager, resource_type: &str, version: Option<&str>, } let mut conf = Configuration::new(); - if let Err(err) = add_resource_export_results_to_configuration(dsc_resource, &mut conf, input) { + let execution_information = ExecutionInformation::new(); + if let Err(err) = add_resource_export_results_to_configuration(dsc_resource, &mut conf, input, &execution_information) { error!("{err}"); exit(EXIT_DSC_ERROR); } diff --git a/dsc/tests/dsc_config_export.tests.ps1 b/dsc/tests/dsc_config_export.tests.ps1 new file mode 100644 index 000000000..f0a3baf89 --- /dev/null +++ b/dsc/tests/dsc_config_export.tests.ps1 @@ -0,0 +1,34 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +Describe 'config export tests' { + It 'Execution information is included in config export results' { + $config_yaml = @' +$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json +resources: +- name: os + type: Microsoft/OSInfo +'@ + + $out = dsc config export -i $config_yaml | ConvertFrom-Json + $LASTEXITCODE | Should -Be 0 + $out.executionInformation | Should -Not -BeNullOrEmpty + $out.executionInformation.startDatetime | Should -Not -BeNullOrEmpty + $out.executionInformation.endDatetime | Should -Not -BeNullOrEmpty + $out.executionInformation.duration | Should -Not -BeNullOrEmpty + $out.executionInformation.operation | Should -BeExactly 'export' + $out.executionInformation.executionType | Should -BeExactly 'actual' + $out.executionInformation.securityContext | Should -Not -BeNullOrEmpty + $out.executionInformation.version | Should -BeExactly (dsc --version).replace("dsc ", "") + $out.resources | Should -Not -BeNullOrEmpty + $out.resources.count | Should -Be 1 + $out.resources[0].Name | Should -Not -BeNullOrEmpty + $out.resources[0].type | Should -BeExactly 'Microsoft/OSInfo' + $out.resources[0].executionInformation | Should -Not -BeNullOrEmpty + $out.resources[0].executionInformation.duration | Should -Not -BeNullOrEmpty + $out.resources[0].properties.family | Should -BeIn @('Windows', 'Linux', 'macOS') + $out.resources[0].properties.architecture | Should -BeIn @('x64', 'arm64') + $out.resources[0].properties.version | Should -Not -BeNullOrEmpty + $out.resources[0].properties.bitness | Should -BeIn @(32, 64) + } +} diff --git a/lib/dsc-lib/src/configure/config_doc.rs b/lib/dsc-lib/src/configure/config_doc.rs index a4d2b42fb..5c1b93ca6 100644 --- a/lib/dsc-lib/src/configure/config_doc.rs +++ b/lib/dsc-lib/src/configure/config_doc.rs @@ -296,7 +296,7 @@ pub struct Output { } #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] -#[serde(deny_unknown_fields)] +#[serde(deny_unknown_fields, rename_all = "camelCase")] #[dsc_repo_schema( base_name = "document", folder_path = "config", @@ -464,7 +464,7 @@ pub struct Sku { } #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] -#[serde(deny_unknown_fields)] +#[serde(deny_unknown_fields, rename_all = "camelCase")] #[dsc_repo_schema(base_name = "document.resource", folder_path = "config")] pub struct Resource { #[serde(skip_serializing_if = "Option::is_none")] @@ -472,7 +472,7 @@ pub struct Resource { /// The fully qualified name of the resource type #[serde(rename = "type")] pub resource_type: FullyQualifiedTypeName, - #[serde(skip_serializing_if = "Option::is_none", rename = "requireVersion", alias = "apiVersion")] + #[serde(skip_serializing_if = "Option::is_none", alias = "apiVersion")] pub require_version: Option, /// A friendly name for the resource instance #[serde(default)] diff --git a/lib/dsc-lib/src/configure/mod.rs b/lib/dsc-lib/src/configure/mod.rs index 3dd32ab43..4c94e6918 100644 --- a/lib/dsc-lib/src/configure/mod.rs +++ b/lib/dsc-lib/src/configure/mod.rs @@ -58,7 +58,7 @@ pub struct Configurator { /// # Errors /// /// This function will return an error if the underlying resource fails. -pub fn add_resource_export_results_to_configuration(resource: &DscResource, conf: &mut Configuration, input: &str) -> Result { +pub fn add_resource_export_results_to_configuration(resource: &DscResource, conf: &mut Configuration, input: &str, execution_information: &ExecutionInformation) -> Result { let export_result = resource.export(input)?; @@ -105,7 +105,7 @@ pub fn add_resource_export_results_to_configuration(resource: &DscResource, conf } r.properties = escape_property_values(&props)?; let mut properties = serde_json::to_value(&r.properties)?; - let mut execution_information = ExecutionInformation::new(); + let mut execution_information = execution_information.clone(); get_metadata_from_result(None, &mut properties, &mut metadata, &mut execution_information)?; r.properties = Some(properties.as_object().cloned().unwrap_or_default()); r.metadata = if metadata.microsoft.is_some() || !metadata.other.is_empty() { @@ -868,9 +868,12 @@ impl Configurator { }; let properties = self.get_properties(resource, &dsc_resource.kind)?; debug!("resource_type {}", &resource.resource_type); + let start_datetime = chrono::Local::now(); let input = add_metadata(&dsc_resource, properties, resource.metadata.clone())?; + let end_datetime = chrono::Local::now(); + let execution_information = ExecutionInformation::new_with_duration(&start_datetime, &end_datetime); trace!("{}", t!("configure.mod.exportInput", input = input)); - let export_result = match add_resource_export_results_to_configuration(&dsc_resource, &mut conf, input.as_str()) { + let export_result = match add_resource_export_results_to_configuration(&dsc_resource, &mut conf, input.as_str(), &execution_information) { Ok(result) => result, Err(e) => { progress.set_failure(get_failure_from_error(&e)); @@ -894,6 +897,9 @@ impl Configurator { }, } + let mut execution_information = ExecutionInformation::new(); + self.get_execution_information(Operation::Export, &mut execution_information); + conf.execution_information = Some(execution_information); result.result = Some(conf); self.process_output()?; if !self.context.outputs.is_empty() { From 4209768b545b1f71f97327c9c752487aa3532652 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Thu, 19 Mar 2026 15:57:57 -0700 Subject: [PATCH 2/2] address copilot feedback, fix test issue --- dsc/src/resource_command.rs | 5 ++--- dsc/tests/dsc_config_export.tests.ps1 | 2 +- lib/dsc-lib/src/configure/mod.rs | 16 +++++++++------- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/dsc/src/resource_command.rs b/dsc/src/resource_command.rs index 570e81ec4..cc4c0216a 100644 --- a/dsc/src/resource_command.rs +++ b/dsc/src/resource_command.rs @@ -3,7 +3,7 @@ use crate::args::{GetOutputFormat, OutputFormat}; use crate::util::{EXIT_DSC_ERROR, EXIT_INVALID_ARGS, EXIT_JSON_ERROR, EXIT_DSC_RESOURCE_NOT_FOUND, write_object}; -use dsc_lib::configure::config_doc::{Configuration, ExecutionInformation, ExecutionKind}; +use dsc_lib::configure::config_doc::{Configuration, ExecutionKind}; use dsc_lib::configure::add_resource_export_results_to_configuration; use dsc_lib::discovery::discovery_trait::DiscoveryFilter; use dsc_lib::dscresources::{resource_manifest::Kind, invoke_result::{DeleteResultKind, GetResult, ResourceGetResponse, ResourceSetResponse, SetResult}}; @@ -339,8 +339,7 @@ pub fn export(dsc: &mut DscManager, resource_type: &str, version: Option<&str>, } let mut conf = Configuration::new(); - let execution_information = ExecutionInformation::new(); - if let Err(err) = add_resource_export_results_to_configuration(dsc_resource, &mut conf, input, &execution_information) { + if let Err(err) = add_resource_export_results_to_configuration(dsc_resource, &mut conf, input) { error!("{err}"); exit(EXIT_DSC_ERROR); } diff --git a/dsc/tests/dsc_config_export.tests.ps1 b/dsc/tests/dsc_config_export.tests.ps1 index f0a3baf89..8fcf2326d 100644 --- a/dsc/tests/dsc_config_export.tests.ps1 +++ b/dsc/tests/dsc_config_export.tests.ps1 @@ -27,7 +27,7 @@ resources: $out.resources[0].executionInformation | Should -Not -BeNullOrEmpty $out.resources[0].executionInformation.duration | Should -Not -BeNullOrEmpty $out.resources[0].properties.family | Should -BeIn @('Windows', 'Linux', 'macOS') - $out.resources[0].properties.architecture | Should -BeIn @('x64', 'arm64') + $out.resources[0].properties.architecture | Should -BeIn @('x86_64', 'arm64') $out.resources[0].properties.version | Should -Not -BeNullOrEmpty $out.resources[0].properties.bitness | Should -BeIn @(32, 64) } diff --git a/lib/dsc-lib/src/configure/mod.rs b/lib/dsc-lib/src/configure/mod.rs index 4c94e6918..46b3968b3 100644 --- a/lib/dsc-lib/src/configure/mod.rs +++ b/lib/dsc-lib/src/configure/mod.rs @@ -50,6 +50,7 @@ pub struct Configurator { /// /// * `resource` - The resource to export. /// * `conf` - The configuration to add the results to. +/// * `input` - The input to the export operation. /// /// # Panics /// @@ -58,13 +59,17 @@ pub struct Configurator { /// # Errors /// /// This function will return an error if the underlying resource fails. -pub fn add_resource_export_results_to_configuration(resource: &DscResource, conf: &mut Configuration, input: &str, execution_information: &ExecutionInformation) -> Result { +pub fn add_resource_export_results_to_configuration(resource: &DscResource, conf: &mut Configuration, input: &str) -> Result { + let start_datetime = chrono::Local::now(); let export_result = resource.export(input)?; + let end_datetime = chrono::Local::now(); if resource.kind == Kind::Exporter { for instance in &export_result.actual_state { - let resource = serde_json::from_value::(instance.clone())?; + let mut resource = serde_json::from_value::(instance.clone())?; + let execution_information = ExecutionInformation::new_with_duration(&start_datetime, &end_datetime); + resource.execution_information = Some(execution_information); conf.resources.push(resource); } } else { @@ -105,7 +110,7 @@ pub fn add_resource_export_results_to_configuration(resource: &DscResource, conf } r.properties = escape_property_values(&props)?; let mut properties = serde_json::to_value(&r.properties)?; - let mut execution_information = execution_information.clone(); + let mut execution_information = ExecutionInformation::new_with_duration(&start_datetime, &end_datetime); get_metadata_from_result(None, &mut properties, &mut metadata, &mut execution_information)?; r.properties = Some(properties.as_object().cloned().unwrap_or_default()); r.metadata = if metadata.microsoft.is_some() || !metadata.other.is_empty() { @@ -868,12 +873,9 @@ impl Configurator { }; let properties = self.get_properties(resource, &dsc_resource.kind)?; debug!("resource_type {}", &resource.resource_type); - let start_datetime = chrono::Local::now(); let input = add_metadata(&dsc_resource, properties, resource.metadata.clone())?; - let end_datetime = chrono::Local::now(); - let execution_information = ExecutionInformation::new_with_duration(&start_datetime, &end_datetime); trace!("{}", t!("configure.mod.exportInput", input = input)); - let export_result = match add_resource_export_results_to_configuration(&dsc_resource, &mut conf, input.as_str(), &execution_information) { + let export_result = match add_resource_export_results_to_configuration(&dsc_resource, &mut conf, input.as_str()) { Ok(result) => result, Err(e) => { progress.set_failure(get_failure_from_error(&e));