From 0cd3fd775bd9ec290d5350c859445489d4116108 Mon Sep 17 00:00:00 2001 From: Tess Gauthier Date: Thu, 30 May 2024 11:58:44 -0400 Subject: [PATCH 1/7] add resource support for what-if through additional parameter during config set --- dsc/tests/dsc_whatif.tests.ps1 | 36 +++++++++++++++++++ dsc_lib/src/dscresources/command_resource.rs | 18 +++++++--- dsc_lib/src/dscresources/dscresource.rs | 2 ++ dsc_lib/src/dscresources/resource_manifest.rs | 3 ++ tools/dsctest/dscwhatif.dsc.resource.json | 30 ++++++++++++++++ tools/dsctest/src/args.rs | 7 ++++ tools/dsctest/src/main.rs | 13 +++++++ tools/dsctest/src/whatif.rs | 21 +++++++++++ 8 files changed, 125 insertions(+), 5 deletions(-) create mode 100644 tools/dsctest/dscwhatif.dsc.resource.json create mode 100644 tools/dsctest/src/whatif.rs diff --git a/dsc/tests/dsc_whatif.tests.ps1 b/dsc/tests/dsc_whatif.tests.ps1 index ff0f979c6..fc9aa427d 100644 --- a/dsc/tests/dsc_whatif.tests.ps1 +++ b/dsc/tests/dsc_whatif.tests.ps1 @@ -67,4 +67,40 @@ Describe 'whatif tests' { $result | Should -Match 'ERROR.*?Not implemented.*?what-if' $LASTEXITCODE | Should -Be 2 } + + It 'actual execution of WhatIf resource' { + $config_yaml = @" + `$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/10/config/document.json + resources: + - name: WhatIf + type: Test/WhatIf + properties: + executionType: Actual +"@ + $result = $config_yaml | dsc config set | ConvertFrom-Json + $result.metadata.'Microsoft.DSC'.executionType | Should -BeExactly 'Actual' + $result.results.result.afterState.executionType | Should -BeExactly 'Actual' + $result.results.result.changedProperties | Should -Be $null + $result.hadErrors | Should -BeFalse + $result.results.Count | Should -Be 1 + $LASTEXITCODE | Should -Be 0 + } + + It 'what-if execution of WhatIf resource' { + $config_yaml = @" + `$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/10/config/document.json + resources: + - name: WhatIf + type: Test/WhatIf + properties: + executionType: Actual +"@ + $result = $config_yaml | dsc config set -w | ConvertFrom-Json + $result.metadata.'Microsoft.DSC'.executionType | Should -BeExactly 'WhatIf' + $result.results.result.afterState.executionType | Should -BeExactly 'WhatIf' + $result.results.result.changedProperties | Should -BeExactly 'executionType' + $result.hadErrors | Should -BeFalse + $result.results.Count | Should -Be 1 + $LASTEXITCODE | Should -Be 0 + } } diff --git a/dsc_lib/src/dscresources/command_resource.rs b/dsc_lib/src/dscresources/command_resource.rs index ce3196136..d38b64079 100644 --- a/dsc_lib/src/dscresources/command_resource.rs +++ b/dsc_lib/src/dscresources/command_resource.rs @@ -105,7 +105,7 @@ pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_te if !skip_test && set.pre_test != Some(true) { info!("No pretest, invoking test {}", &resource.resource_type); let test_result = invoke_test(resource, cwd, desired)?; - if execution_type == &ExecutionKind::WhatIf { + if execution_type == &ExecutionKind::WhatIf && set.handles_what_if != Some(true) { return Ok(test_result.into()); } let (in_desired_state, actual_state) = match test_result { @@ -130,9 +130,8 @@ pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_te } } - if ExecutionKind::WhatIf == *execution_type { - // TODO: continue execution when resources can implement what-if; only return an error here temporarily - return Err(DscError::NotImplemented("what-if not yet supported for resources that implement pre-test".to_string())); + if ExecutionKind::WhatIf == *execution_type && set.handles_what_if != Some(true) { + return Err(DscError::NotImplemented("cannot process what-if execution type, resource does not implement what-if or pre-test".to_string())); } let Some(get) = &resource.get else { @@ -158,7 +157,16 @@ pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_te let mut env: Option> = None; let mut input_desired: Option<&str> = None; - let args = process_args(&set.args, desired); + let mut args = process_args(&set.args, desired); + if ExecutionKind::WhatIf == *execution_type { + if let Some(mut arguments) = args { + arguments.push("--what-if".to_string()); + args = Some(arguments); + } + else { + args = Some(vec!["--what-if".to_string()]); + } + } match &set.input { Some(InputKind::Env) => { env = Some(json_to_hashmap(desired)?); diff --git a/dsc_lib/src/dscresources/dscresource.rs b/dsc_lib/src/dscresources/dscresource.rs index 6148fbd07..10f2bda2a 100644 --- a/dsc_lib/src/dscresources/dscresource.rs +++ b/dsc_lib/src/dscresources/dscresource.rs @@ -50,6 +50,8 @@ pub enum Capability { Set, /// The resource supports the `_exist` property directly. SetHandlesExist, + /// The resource supports the `what-if` execution type directly. + SetHandlesWhatIf, /// The resource supports validating configuration. Test, /// The resource supports deleting configuration. diff --git a/dsc_lib/src/dscresources/resource_manifest.rs b/dsc_lib/src/dscresources/resource_manifest.rs index 0060659c8..e266bdb31 100644 --- a/dsc_lib/src/dscresources/resource_manifest.rs +++ b/dsc_lib/src/dscresources/resource_manifest.rs @@ -171,6 +171,9 @@ pub struct SetMethod { /// Indicates that the resource directly handles `_exist` as a property. #[serde(rename = "handlesExist", skip_serializing_if = "Option::is_none")] pub handles_exist: Option, + /// Indicates that the resource directly handles `what-if` execution type. + #[serde(rename = "handlesWhatIf", skip_serializing_if = "Option::is_none")] + pub handles_what_if: Option, /// The type of return value expected from the Set method. #[serde(rename = "return", skip_serializing_if = "Option::is_none")] pub returns: Option, diff --git a/tools/dsctest/dscwhatif.dsc.resource.json b/tools/dsctest/dscwhatif.dsc.resource.json new file mode 100644 index 000000000..093b0efd4 --- /dev/null +++ b/tools/dsctest/dscwhatif.dsc.resource.json @@ -0,0 +1,30 @@ +{ + "$schema": "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/bundled/resource/manifest.json", + "type": "Test/WhatIf", + "version": "0.1.0", + "get": { + "executable": "dsctest", + "args": [ + "what-if" + ] + }, + "set": { + "executable": "dsctest", + "args": [ + "what-if" + ], + "handlesWhatIf": true, + "implementsPretest": true, + "return": "state" + }, + "schema": { + "command": { + "executable": "dsctest", + "args": [ + "schema", + "-s", + "what-if" + ] + } + } +} diff --git a/tools/dsctest/src/args.rs b/tools/dsctest/src/args.rs index f74c508b5..2bbbe2c60 100644 --- a/tools/dsctest/src/args.rs +++ b/tools/dsctest/src/args.rs @@ -10,6 +10,7 @@ pub enum Schemas { Exist, Sleep, Trace, + WhatIf, } #[derive(Debug, Parser)] @@ -54,4 +55,10 @@ pub enum SubCommand { #[clap(name = "trace", about = "The trace level")] Trace, + + #[clap(name = "what-if", about = "Check if it is a what-if operation")] + WhatIf { + #[clap(name = "what-if", short, long, help = "The input to the what-if command as JSON")] + what_if: bool, + } } diff --git a/tools/dsctest/src/main.rs b/tools/dsctest/src/main.rs index 79684cb32..cd3790d24 100644 --- a/tools/dsctest/src/main.rs +++ b/tools/dsctest/src/main.rs @@ -7,6 +7,7 @@ mod echo; mod exist; mod sleep; mod trace; +mod whatif; use args::{Args, Schemas, SubCommand}; use clap::Parser; @@ -16,6 +17,7 @@ use crate::echo::Echo; use crate::exist::{Exist, State}; use crate::sleep::Sleep; use crate::trace::Trace; +use crate::whatif::WhatIf; use std::{thread, time::Duration}; fn main() { @@ -75,6 +77,9 @@ fn main() { Schemas::Trace => { schema_for!(Trace) }, + Schemas::WhatIf => { + schema_for!(WhatIf) + }, }; serde_json::to_string(&schema).unwrap() }, @@ -100,6 +105,14 @@ fn main() { }; serde_json::to_string(&trace).unwrap() }, + SubCommand::WhatIf { what_if } => { + let result: WhatIf = if what_if { + WhatIf { execution_type: "WhatIf".to_string() } + } else { + WhatIf { execution_type: "Actual".to_string() } + }; + serde_json::to_string(&result).unwrap() + }, }; println!("{json}"); diff --git a/tools/dsctest/src/whatif.rs b/tools/dsctest/src/whatif.rs new file mode 100644 index 000000000..8ffef6e58 --- /dev/null +++ b/tools/dsctest/src/whatif.rs @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use schemars::JsonSchema; +use serde::{Deserialize, Serialize}; + +// #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +// #[serde(untagged)] +// pub enum ExecutionKind { +// #[serde(rename = "actual")] +// Actual, +// #[serde(rename = "whatIf")] +// WhatIf, +// } + +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[serde(deny_unknown_fields)] +pub struct WhatIf { + #[serde(rename = "executionType")] + pub execution_type: String, +} From 1539757d40f40f1e60008e462a3e0913183a0221 Mon Sep 17 00:00:00 2001 From: Tess Gauthier Date: Thu, 30 May 2024 15:19:59 -0400 Subject: [PATCH 2/7] add what-if argkind --- dsc/src/subcommand.rs | 5 ++- dsc_lib/src/configure/config_doc.rs | 5 ++- dsc_lib/src/configure/mod.rs | 2 +- dsc_lib/src/discovery/command_discovery.rs | 10 ++++- dsc_lib/src/dscresources/command_resource.rs | 38 +++++++++---------- dsc_lib/src/dscresources/dscresource.rs | 8 +++- dsc_lib/src/dscresources/resource_manifest.rs | 5 +++ tools/dsctest/dscwhatif.dsc.resource.json | 6 ++- tools/dsctest/src/args.rs | 2 +- tools/dsctest/src/whatif.rs | 9 ----- 10 files changed, 51 insertions(+), 39 deletions(-) diff --git a/dsc/src/subcommand.rs b/dsc/src/subcommand.rs index 2cf9ba5d8..3c235de28 100644 --- a/dsc/src/subcommand.rs +++ b/dsc/src/subcommand.rs @@ -250,7 +250,7 @@ pub fn config(subcommand: &ConfigSubCommand, parameters: &Option, stdin: if let ConfigSubCommand::Set { what_if , .. } = subcommand { if *what_if { - configurator.context.execution_type = ExecutionKind::WhatIf; + configurator.context.execution_type = ExecutionKind::WhatIfDSC; } }; @@ -511,11 +511,12 @@ fn list_resources(dsc: &mut DscManager, resource_name: &Option, adapter_ write_table = true; } for resource in dsc.list_available_resources(&resource_name.clone().unwrap_or("*".to_string()), &adapter_name.clone().unwrap_or_default()) { - let mut capabilities = "-------".to_string(); + let mut capabilities = "--------".to_string(); let capability_types = [ (Capability::Get, "g"), (Capability::Set, "s"), (Capability::SetHandlesExist, "x"), + (Capability::SetHandlesWhatIf, "w"), (Capability::Test, "t"), (Capability::Delete, "d"), (Capability::Export, "e"), diff --git a/dsc_lib/src/configure/config_doc.rs b/dsc_lib/src/configure/config_doc.rs index 693b86eea..464e7c55a 100644 --- a/dsc_lib/src/configure/config_doc.rs +++ b/dsc_lib/src/configure/config_doc.rs @@ -30,7 +30,10 @@ pub enum Operation { #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] pub enum ExecutionKind { Actual, - WhatIf, + #[serde(rename = "WhatIf")] + WhatIfDSC, + #[serde(rename = "WhatIf")] + WhatIfResource, } #[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] diff --git a/dsc_lib/src/configure/mod.rs b/dsc_lib/src/configure/mod.rs index 6e99670e0..624a6d564 100644 --- a/dsc_lib/src/configure/mod.rs +++ b/dsc_lib/src/configure/mod.rs @@ -318,7 +318,7 @@ impl Configurator { set_result = dsc_resource.set(&desired, skip_test, &self.context.execution_type)?; end_datetime = chrono::Local::now(); } else if dsc_resource.capabilities.contains(&Capability::Delete) { - if self.context.execution_type == ExecutionKind::WhatIf { + if self.context.execution_type == ExecutionKind::WhatIfDSC { // TODO: add delete what-if support return Err(DscError::NotSupported("What-if execution not supported for delete".to_string())); } diff --git a/dsc_lib/src/discovery/command_discovery.rs b/dsc_lib/src/discovery/command_discovery.rs index ea1ae1c00..44bd53bff 100644 --- a/dsc_lib/src/discovery/command_discovery.rs +++ b/dsc_lib/src/discovery/command_discovery.rs @@ -4,7 +4,7 @@ use crate::discovery::discovery_trait::ResourceDiscovery; use crate::discovery::convert_wildcard_to_regex; use crate::dscresources::dscresource::{Capability, DscResource, ImplementedAs}; -use crate::dscresources::resource_manifest::{import_manifest, validate_semver, Kind, ResourceManifest}; +use crate::dscresources::resource_manifest::{import_manifest, validate_semver, ArgKind, Kind, ResourceManifest}; use crate::dscresources::command_resource::invoke_command; use crate::dscerror::DscError; use indicatif::ProgressStyle; @@ -449,6 +449,14 @@ fn load_manifest(path: &Path) -> Result { if set.handles_exist == Some(true) { capabilities.push(Capability::SetHandlesExist); } + if let Some(arg_values) = &set.args { + for arg in arg_values { + if let &ArgKind::WhatIf { .. } = arg { + capabilities.push(Capability::SetHandlesWhatIf); + break; + } + } + } } if manifest.test.is_some() { capabilities.push(Capability::Test); diff --git a/dsc_lib/src/dscresources/command_resource.rs b/dsc_lib/src/dscresources/command_resource.rs index d38b64079..ad262121d 100644 --- a/dsc_lib/src/dscresources/command_resource.rs +++ b/dsc_lib/src/dscresources/command_resource.rs @@ -50,7 +50,7 @@ pub fn invoke_get(resource: &ResourceManifest, cwd: &str, filter: &str) -> Resul let Some(get) = &resource.get else { return Err(DscError::NotImplemented("get".to_string())); }; - let args = process_args(&get.args, filter); + let args = process_args(&get.args, filter, &ExecutionKind::Actual); if !filter.is_empty() { verify_json(resource, cwd, filter)?; command_input = get_command_input(&get.input, filter)?; @@ -105,7 +105,7 @@ pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_te if !skip_test && set.pre_test != Some(true) { info!("No pretest, invoking test {}", &resource.resource_type); let test_result = invoke_test(resource, cwd, desired)?; - if execution_type == &ExecutionKind::WhatIf && set.handles_what_if != Some(true) { + if execution_type == &ExecutionKind::WhatIfDSC { return Ok(test_result.into()); } let (in_desired_state, actual_state) = match test_result { @@ -130,14 +130,14 @@ pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_te } } - if ExecutionKind::WhatIf == *execution_type && set.handles_what_if != Some(true) { + if ExecutionKind::WhatIfDSC == *execution_type { return Err(DscError::NotImplemented("cannot process what-if execution type, resource does not implement what-if or pre-test".to_string())); } let Some(get) = &resource.get else { return Err(DscError::NotImplemented("get".to_string())); }; - let args = process_args(&get.args, desired); + let args = process_args(&get.args, desired, &ExecutionKind::Actual); let command_input = get_command_input(&get.input, desired)?; info!("Getting current state for set by invoking get {} using {}", &resource.resource_type, &get.executable); @@ -157,16 +157,7 @@ pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_te let mut env: Option> = None; let mut input_desired: Option<&str> = None; - let mut args = process_args(&set.args, desired); - if ExecutionKind::WhatIf == *execution_type { - if let Some(mut arguments) = args { - arguments.push("--what-if".to_string()); - args = Some(arguments); - } - else { - args = Some(vec!["--what-if".to_string()]); - } - } + let args = process_args(&set.args, desired, execution_type); match &set.input { Some(InputKind::Env) => { env = Some(json_to_hashmap(desired)?); @@ -269,7 +260,7 @@ pub fn invoke_test(resource: &ResourceManifest, cwd: &str, expected: &str) -> Re verify_json(resource, cwd, expected)?; - let args = process_args(&test.args, expected); + let args = process_args(&test.args, expected, &ExecutionKind::Actual); let command_input = get_command_input(&test.input, expected)?; info!("Invoking test '{}' using '{}'", &resource.resource_type, &test.executable); @@ -383,7 +374,7 @@ pub fn invoke_delete(resource: &ResourceManifest, cwd: &str, filter: &str) -> Re verify_json(resource, cwd, filter)?; - let args = process_args(&delete.args, filter); + let args = process_args(&delete.args, filter, &ExecutionKind::Actual); let command_input = get_command_input(&delete.input, filter)?; info!("Invoking delete '{}' using '{}'", &resource.resource_type, &delete.executable); @@ -414,7 +405,7 @@ pub fn invoke_validate(resource: &ResourceManifest, cwd: &str, config: &str) -> return Err(DscError::NotImplemented("validate".to_string())); }; - let args = process_args(&validate.args, config); + let args = process_args(&validate.args, config, &ExecutionKind::Actual); let command_input = get_command_input(&validate.input, config)?; info!("Invoking validate '{}' using '{}'", &resource.resource_type, &validate.executable); @@ -489,9 +480,9 @@ pub fn invoke_export(resource: &ResourceManifest, cwd: &str, input: Option<&str> command_input = get_command_input(&export.input, input)?; } - args = process_args(&export.args, input); + args = process_args(&export.args, input, &ExecutionKind::Actual); } else { - args = process_args(&export.args, ""); + args = process_args(&export.args, "", &ExecutionKind::Actual); } let (_exit_code, stdout, stderr) = invoke_command(&export.executable, args, command_input.stdin.as_deref(), Some(cwd), command_input.env)?; @@ -536,7 +527,7 @@ pub fn invoke_resolve(resource: &ResourceManifest, cwd: &str, input: &str) -> Re return Err(DscError::Operation(format!("Resolve is not supported by resource {}", &resource.resource_type))); }; - let args = process_args(&resolve.args, input); + let args = process_args(&resolve.args, input, &ExecutionKind::Actual); let command_input = get_command_input(&resolve.input, input)?; info!("Invoking resolve '{}' using '{}'", &resource.resource_type, &resolve.executable); @@ -628,7 +619,7 @@ pub fn invoke_command(executable: &str, args: Option>, input: Option Ok((exit_code, stdout, cleaned_stderr)) } -fn process_args(args: &Option>, value: &str) -> Option> { +fn process_args(args: &Option>, value: &str, execution_type: &ExecutionKind) -> Option> { let Some(arg_values) = args else { debug!("No args to process"); return None; @@ -648,6 +639,11 @@ fn process_args(args: &Option>, value: &str) -> Option> processed_args.push(json_input_arg.clone()); processed_args.push(value.to_string()); }, + ArgKind::WhatIf { what_if_input_arg} => { + if execution_type == &ExecutionKind::WhatIfResource { + processed_args.push(what_if_input_arg.clone()); + } + } } } diff --git a/dsc_lib/src/dscresources/dscresource.rs b/dsc_lib/src/dscresources/dscresource.rs index 10f2bda2a..e5516a133 100644 --- a/dsc_lib/src/dscresources/dscresource.rs +++ b/dsc_lib/src/dscresources/dscresource.rs @@ -211,7 +211,13 @@ impl Invoke for DscResource { return Err(DscError::MissingManifest(self.type_name.clone())); }; let resource_manifest = import_manifest(manifest.clone())?; - command_resource::invoke_set(&resource_manifest, &self.directory, desired, skip_test, execution_type) + let execution = if self.capabilities.contains(&Capability::SetHandlesWhatIf) && execution_type == &ExecutionKind::WhatIfDSC { + ExecutionKind::WhatIfResource + } + else { + execution_type.clone() + }; + command_resource::invoke_set(&resource_manifest, &self.directory, desired, skip_test, &execution) }, } } diff --git a/dsc_lib/src/dscresources/resource_manifest.rs b/dsc_lib/src/dscresources/resource_manifest.rs index e266bdb31..a9b45c429 100644 --- a/dsc_lib/src/dscresources/resource_manifest.rs +++ b/dsc_lib/src/dscresources/resource_manifest.rs @@ -103,6 +103,11 @@ pub enum ArgKind { /// Indicates if argument is mandatory which will pass an empty string if no JSON input is provided. Default is false. mandatory: Option, }, + WhatIf { + /// The argument that serves as the what-if switch. + #[serde(rename = "whatIfInputArg")] + what_if_input_arg: String, + } } #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] diff --git a/tools/dsctest/dscwhatif.dsc.resource.json b/tools/dsctest/dscwhatif.dsc.resource.json index 093b0efd4..a93d46e6d 100644 --- a/tools/dsctest/dscwhatif.dsc.resource.json +++ b/tools/dsctest/dscwhatif.dsc.resource.json @@ -11,9 +11,11 @@ "set": { "executable": "dsctest", "args": [ - "what-if" + "what-if", + { + "whatIfInputArg": "--what-if" + } ], - "handlesWhatIf": true, "implementsPretest": true, "return": "state" }, diff --git a/tools/dsctest/src/args.rs b/tools/dsctest/src/args.rs index 2bbbe2c60..5e4283e6a 100644 --- a/tools/dsctest/src/args.rs +++ b/tools/dsctest/src/args.rs @@ -58,7 +58,7 @@ pub enum SubCommand { #[clap(name = "what-if", about = "Check if it is a what-if operation")] WhatIf { - #[clap(name = "what-if", short, long, help = "The input to the what-if command as JSON")] + #[clap(name = "what-if", short, long, help = "Run as a what-if executionType instead of actual executionType")] what_if: bool, } } diff --git a/tools/dsctest/src/whatif.rs b/tools/dsctest/src/whatif.rs index 8ffef6e58..fd37bf1ee 100644 --- a/tools/dsctest/src/whatif.rs +++ b/tools/dsctest/src/whatif.rs @@ -4,15 +4,6 @@ use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -// #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] -// #[serde(untagged)] -// pub enum ExecutionKind { -// #[serde(rename = "actual")] -// Actual, -// #[serde(rename = "whatIf")] -// WhatIf, -// } - #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] #[serde(deny_unknown_fields)] pub struct WhatIf { From 18006f804a4098bf3e62cfbaf0fc735568dd4a70 Mon Sep 17 00:00:00 2001 From: Tess Gauthier Date: Thu, 30 May 2024 15:57:10 -0400 Subject: [PATCH 3/7] add comments --- dsc_lib/src/configure/config_doc.rs | 1 + dsc_lib/src/dscresources/command_resource.rs | 4 ++-- dsc_lib/src/dscresources/resource_manifest.rs | 3 --- tools/dsctest/dscwhatif.dsc.resource.json | 1 - 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/dsc_lib/src/configure/config_doc.rs b/dsc_lib/src/configure/config_doc.rs index 464e7c55a..fb7ec6f18 100644 --- a/dsc_lib/src/configure/config_doc.rs +++ b/dsc_lib/src/configure/config_doc.rs @@ -30,6 +30,7 @@ pub enum Operation { #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] pub enum ExecutionKind { Actual, + // differentiate internally whether what-if should be processed by the resource or dsc engine #[serde(rename = "WhatIf")] WhatIfDSC, #[serde(rename = "WhatIf")] diff --git a/dsc_lib/src/dscresources/command_resource.rs b/dsc_lib/src/dscresources/command_resource.rs index ad262121d..9e9a540ea 100644 --- a/dsc_lib/src/dscresources/command_resource.rs +++ b/dsc_lib/src/dscresources/command_resource.rs @@ -131,7 +131,7 @@ pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_te } if ExecutionKind::WhatIfDSC == *execution_type { - return Err(DscError::NotImplemented("cannot process what-if execution type, resource does not implement what-if or pre-test".to_string())); + return Err(DscError::NotImplemented("cannot process what-if execution type, as resource implements pre-test and does not support what-if".to_string())); } let Some(get) = &resource.get else { @@ -639,7 +639,7 @@ fn process_args(args: &Option>, value: &str, execution_type: &Execu processed_args.push(json_input_arg.clone()); processed_args.push(value.to_string()); }, - ArgKind::WhatIf { what_if_input_arg} => { + ArgKind::WhatIf { what_if_input_arg } => { if execution_type == &ExecutionKind::WhatIfResource { processed_args.push(what_if_input_arg.clone()); } diff --git a/dsc_lib/src/dscresources/resource_manifest.rs b/dsc_lib/src/dscresources/resource_manifest.rs index a9b45c429..42ee026e1 100644 --- a/dsc_lib/src/dscresources/resource_manifest.rs +++ b/dsc_lib/src/dscresources/resource_manifest.rs @@ -176,9 +176,6 @@ pub struct SetMethod { /// Indicates that the resource directly handles `_exist` as a property. #[serde(rename = "handlesExist", skip_serializing_if = "Option::is_none")] pub handles_exist: Option, - /// Indicates that the resource directly handles `what-if` execution type. - #[serde(rename = "handlesWhatIf", skip_serializing_if = "Option::is_none")] - pub handles_what_if: Option, /// The type of return value expected from the Set method. #[serde(rename = "return", skip_serializing_if = "Option::is_none")] pub returns: Option, diff --git a/tools/dsctest/dscwhatif.dsc.resource.json b/tools/dsctest/dscwhatif.dsc.resource.json index a93d46e6d..b360614da 100644 --- a/tools/dsctest/dscwhatif.dsc.resource.json +++ b/tools/dsctest/dscwhatif.dsc.resource.json @@ -16,7 +16,6 @@ "whatIfInputArg": "--what-if" } ], - "implementsPretest": true, "return": "state" }, "schema": { From c4b2c5e85f6a54a3ac32fdbe4acad9bab38ef0d2 Mon Sep 17 00:00:00 2001 From: Tess Gauthier Date: Thu, 30 May 2024 17:59:36 -0400 Subject: [PATCH 4/7] update test --- dsc_lib/src/dscresources/command_resource.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dsc_lib/src/dscresources/command_resource.rs b/dsc_lib/src/dscresources/command_resource.rs index 9e9a540ea..9f230af69 100644 --- a/dsc_lib/src/dscresources/command_resource.rs +++ b/dsc_lib/src/dscresources/command_resource.rs @@ -121,7 +121,7 @@ pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_te } }; - if in_desired_state { + if in_desired_state && execution_type == &ExecutionKind::Actual { return Ok(SetResult::Resource(ResourceSetResponse{ before_state: serde_json::from_str(desired)?, after_state: actual_state, From a27220ad1572ea6fbb88d40024e4c065d2c0cc99 Mon Sep 17 00:00:00 2001 From: Tess Gauthier Date: Fri, 31 May 2024 10:30:51 -0400 Subject: [PATCH 5/7] rename what-if arg --- dsc_lib/src/dscresources/resource_manifest.rs | 2 +- tools/dsctest/dscwhatif.dsc.resource.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dsc_lib/src/dscresources/resource_manifest.rs b/dsc_lib/src/dscresources/resource_manifest.rs index 42ee026e1..fc4b8e1cd 100644 --- a/dsc_lib/src/dscresources/resource_manifest.rs +++ b/dsc_lib/src/dscresources/resource_manifest.rs @@ -105,7 +105,7 @@ pub enum ArgKind { }, WhatIf { /// The argument that serves as the what-if switch. - #[serde(rename = "whatIfInputArg")] + #[serde(rename = "whatIfSwitchArg")] what_if_input_arg: String, } } diff --git a/tools/dsctest/dscwhatif.dsc.resource.json b/tools/dsctest/dscwhatif.dsc.resource.json index b360614da..c04bfd8b5 100644 --- a/tools/dsctest/dscwhatif.dsc.resource.json +++ b/tools/dsctest/dscwhatif.dsc.resource.json @@ -13,7 +13,7 @@ "args": [ "what-if", { - "whatIfInputArg": "--what-if" + "whatIfSwitchArg": "--what-if" } ], "return": "state" From 849a35e6bca96450d44b2418642bcadf77240290 Mon Sep 17 00:00:00 2001 From: Tess Gauthier Date: Wed, 5 Jun 2024 14:32:07 -0400 Subject: [PATCH 6/7] add whatif as a separate object in manifest --- dsc/src/subcommand.rs | 4 +- dsc_lib/src/configure/config_doc.rs | 6 +- dsc_lib/src/configure/mod.rs | 2 +- dsc_lib/src/discovery/command_discovery.rs | 13 ++--- dsc_lib/src/dscresources/command_resource.rs | 56 +++++++++++-------- dsc_lib/src/dscresources/dscresource.rs | 12 +--- dsc_lib/src/dscresources/resource_manifest.rs | 8 +-- tools/dsctest/dscwhatif.dsc.resource.json | 15 +++-- tools/dsctest/src/args.rs | 4 +- 9 files changed, 58 insertions(+), 62 deletions(-) diff --git a/dsc/src/subcommand.rs b/dsc/src/subcommand.rs index 3c235de28..d0e935c69 100644 --- a/dsc/src/subcommand.rs +++ b/dsc/src/subcommand.rs @@ -250,7 +250,7 @@ pub fn config(subcommand: &ConfigSubCommand, parameters: &Option, stdin: if let ConfigSubCommand::Set { what_if , .. } = subcommand { if *what_if { - configurator.context.execution_type = ExecutionKind::WhatIfDSC; + configurator.context.execution_type = ExecutionKind::WhatIf; } }; @@ -516,7 +516,7 @@ fn list_resources(dsc: &mut DscManager, resource_name: &Option, adapter_ (Capability::Get, "g"), (Capability::Set, "s"), (Capability::SetHandlesExist, "x"), - (Capability::SetHandlesWhatIf, "w"), + (Capability::WhatIf, "w"), (Capability::Test, "t"), (Capability::Delete, "d"), (Capability::Export, "e"), diff --git a/dsc_lib/src/configure/config_doc.rs b/dsc_lib/src/configure/config_doc.rs index fb7ec6f18..693b86eea 100644 --- a/dsc_lib/src/configure/config_doc.rs +++ b/dsc_lib/src/configure/config_doc.rs @@ -30,11 +30,7 @@ pub enum Operation { #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] pub enum ExecutionKind { Actual, - // differentiate internally whether what-if should be processed by the resource or dsc engine - #[serde(rename = "WhatIf")] - WhatIfDSC, - #[serde(rename = "WhatIf")] - WhatIfResource, + WhatIf, } #[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] diff --git a/dsc_lib/src/configure/mod.rs b/dsc_lib/src/configure/mod.rs index 624a6d564..6e99670e0 100644 --- a/dsc_lib/src/configure/mod.rs +++ b/dsc_lib/src/configure/mod.rs @@ -318,7 +318,7 @@ impl Configurator { set_result = dsc_resource.set(&desired, skip_test, &self.context.execution_type)?; end_datetime = chrono::Local::now(); } else if dsc_resource.capabilities.contains(&Capability::Delete) { - if self.context.execution_type == ExecutionKind::WhatIfDSC { + if self.context.execution_type == ExecutionKind::WhatIf { // TODO: add delete what-if support return Err(DscError::NotSupported("What-if execution not supported for delete".to_string())); } diff --git a/dsc_lib/src/discovery/command_discovery.rs b/dsc_lib/src/discovery/command_discovery.rs index 44bd53bff..241727ac5 100644 --- a/dsc_lib/src/discovery/command_discovery.rs +++ b/dsc_lib/src/discovery/command_discovery.rs @@ -4,7 +4,7 @@ use crate::discovery::discovery_trait::ResourceDiscovery; use crate::discovery::convert_wildcard_to_regex; use crate::dscresources::dscresource::{Capability, DscResource, ImplementedAs}; -use crate::dscresources::resource_manifest::{import_manifest, validate_semver, ArgKind, Kind, ResourceManifest}; +use crate::dscresources::resource_manifest::{import_manifest, validate_semver, Kind, ResourceManifest}; use crate::dscresources::command_resource::invoke_command; use crate::dscerror::DscError; use indicatif::ProgressStyle; @@ -449,14 +449,9 @@ fn load_manifest(path: &Path) -> Result { if set.handles_exist == Some(true) { capabilities.push(Capability::SetHandlesExist); } - if let Some(arg_values) = &set.args { - for arg in arg_values { - if let &ArgKind::WhatIf { .. } = arg { - capabilities.push(Capability::SetHandlesWhatIf); - break; - } - } - } + } + if manifest.whatif.is_some() { + capabilities.push(Capability::WhatIf); } if manifest.test.is_some() { capabilities.push(Capability::Test); diff --git a/dsc_lib/src/dscresources/command_resource.rs b/dsc_lib/src/dscresources/command_resource.rs index 9f230af69..100ffc648 100644 --- a/dsc_lib/src/dscresources/command_resource.rs +++ b/dsc_lib/src/dscresources/command_resource.rs @@ -50,7 +50,7 @@ pub fn invoke_get(resource: &ResourceManifest, cwd: &str, filter: &str) -> Resul let Some(get) = &resource.get else { return Err(DscError::NotImplemented("get".to_string())); }; - let args = process_args(&get.args, filter, &ExecutionKind::Actual); + let args = process_args(&get.args, filter); if !filter.is_empty() { verify_json(resource, cwd, filter)?; command_input = get_command_input(&get.input, filter)?; @@ -95,9 +95,22 @@ pub fn invoke_get(resource: &ResourceManifest, cwd: &str, filter: &str) -> Resul #[allow(clippy::too_many_lines)] pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_test: bool, execution_type: &ExecutionKind) -> Result { // TODO: support import resources - - let Some(set) = &resource.set else { - return Err(DscError::NotImplemented("set".to_string())); + let operation_type: String; + let mut is_synthetic_what_if = false; + let set = match execution_type { + ExecutionKind::Actual => { + operation_type = "set".to_string(); + resource.set.clone().ok_or(DscError::NotImplemented("set".to_string()))? + }, + ExecutionKind::WhatIf => { + operation_type = "whatif".to_string(); + if let Some(whatif) = &resource.whatif { + whatif.clone() + } else { + is_synthetic_what_if = true; + resource.set.clone().ok_or(DscError::NotImplemented("set".to_string()))? + } + } }; verify_json(resource, cwd, desired)?; @@ -105,7 +118,7 @@ pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_te if !skip_test && set.pre_test != Some(true) { info!("No pretest, invoking test {}", &resource.resource_type); let test_result = invoke_test(resource, cwd, desired)?; - if execution_type == &ExecutionKind::WhatIfDSC { + if is_synthetic_what_if { return Ok(test_result.into()); } let (in_desired_state, actual_state) = match test_result { @@ -130,17 +143,17 @@ pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_te } } - if ExecutionKind::WhatIfDSC == *execution_type { + if is_synthetic_what_if { return Err(DscError::NotImplemented("cannot process what-if execution type, as resource implements pre-test and does not support what-if".to_string())); } let Some(get) = &resource.get else { return Err(DscError::NotImplemented("get".to_string())); }; - let args = process_args(&get.args, desired, &ExecutionKind::Actual); + let args = process_args(&get.args, desired); let command_input = get_command_input(&get.input, desired)?; - info!("Getting current state for set by invoking get {} using {}", &resource.resource_type, &get.executable); + info!("Getting current state for {} by invoking get {} using {}", operation_type, &resource.resource_type, &get.executable); let (exit_code, stdout, stderr) = invoke_command(&get.executable, args, command_input.stdin.as_deref(), Some(cwd), command_input.env)?; if resource.kind == Some(Kind::Resource) { @@ -157,7 +170,7 @@ pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_te let mut env: Option> = None; let mut input_desired: Option<&str> = None; - let args = process_args(&set.args, desired, execution_type); + let args = process_args(&set.args, desired); match &set.input { Some(InputKind::Env) => { env = Some(json_to_hashmap(desired)?); @@ -170,21 +183,21 @@ pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_te }, } - info!("Invoking set '{}' using '{}'", &resource.resource_type, &set.executable); + info!("Invoking {} '{}' using '{}'", operation_type, &resource.resource_type, &set.executable); let (exit_code, stdout, stderr) = invoke_command(&set.executable, args, input_desired, Some(cwd), env)?; match set.returns { Some(ReturnKind::State) => { if resource.kind == Some(Kind::Resource) { - debug!("Verifying output of set '{}' using '{}'", &resource.resource_type, &set.executable); + debug!("Verifying output of {} '{}' using '{}'", operation_type, &resource.resource_type, &set.executable); verify_json(resource, cwd, &stdout)?; } let actual_value: Value = match serde_json::from_str(&stdout){ Result::Ok(r) => {r}, Result::Err(err) => { - return Err(DscError::Operation(format!("Failed to parse json from set {}|{}|{} -> {err}", &set.executable, stdout, stderr))) + return Err(DscError::Operation(format!("Failed to parse json from {} '{}'|'{}'|'{}' -> {err}", operation_type, &set.executable, stdout, stderr))) } }; @@ -260,7 +273,7 @@ pub fn invoke_test(resource: &ResourceManifest, cwd: &str, expected: &str) -> Re verify_json(resource, cwd, expected)?; - let args = process_args(&test.args, expected, &ExecutionKind::Actual); + let args = process_args(&test.args, expected); let command_input = get_command_input(&test.input, expected)?; info!("Invoking test '{}' using '{}'", &resource.resource_type, &test.executable); @@ -374,7 +387,7 @@ pub fn invoke_delete(resource: &ResourceManifest, cwd: &str, filter: &str) -> Re verify_json(resource, cwd, filter)?; - let args = process_args(&delete.args, filter, &ExecutionKind::Actual); + let args = process_args(&delete.args, filter); let command_input = get_command_input(&delete.input, filter)?; info!("Invoking delete '{}' using '{}'", &resource.resource_type, &delete.executable); @@ -405,7 +418,7 @@ pub fn invoke_validate(resource: &ResourceManifest, cwd: &str, config: &str) -> return Err(DscError::NotImplemented("validate".to_string())); }; - let args = process_args(&validate.args, config, &ExecutionKind::Actual); + let args = process_args(&validate.args, config); let command_input = get_command_input(&validate.input, config)?; info!("Invoking validate '{}' using '{}'", &resource.resource_type, &validate.executable); @@ -480,9 +493,9 @@ pub fn invoke_export(resource: &ResourceManifest, cwd: &str, input: Option<&str> command_input = get_command_input(&export.input, input)?; } - args = process_args(&export.args, input, &ExecutionKind::Actual); + args = process_args(&export.args, input); } else { - args = process_args(&export.args, "", &ExecutionKind::Actual); + args = process_args(&export.args, ""); } let (_exit_code, stdout, stderr) = invoke_command(&export.executable, args, command_input.stdin.as_deref(), Some(cwd), command_input.env)?; @@ -527,7 +540,7 @@ pub fn invoke_resolve(resource: &ResourceManifest, cwd: &str, input: &str) -> Re return Err(DscError::Operation(format!("Resolve is not supported by resource {}", &resource.resource_type))); }; - let args = process_args(&resolve.args, input, &ExecutionKind::Actual); + let args = process_args(&resolve.args, input); let command_input = get_command_input(&resolve.input, input)?; info!("Invoking resolve '{}' using '{}'", &resource.resource_type, &resolve.executable); @@ -619,7 +632,7 @@ pub fn invoke_command(executable: &str, args: Option>, input: Option Ok((exit_code, stdout, cleaned_stderr)) } -fn process_args(args: &Option>, value: &str, execution_type: &ExecutionKind) -> Option> { +fn process_args(args: &Option>, value: &str) -> Option> { let Some(arg_values) = args else { debug!("No args to process"); return None; @@ -639,11 +652,6 @@ fn process_args(args: &Option>, value: &str, execution_type: &Execu processed_args.push(json_input_arg.clone()); processed_args.push(value.to_string()); }, - ArgKind::WhatIf { what_if_input_arg } => { - if execution_type == &ExecutionKind::WhatIfResource { - processed_args.push(what_if_input_arg.clone()); - } - } } } diff --git a/dsc_lib/src/dscresources/dscresource.rs b/dsc_lib/src/dscresources/dscresource.rs index e5516a133..24506cf9d 100644 --- a/dsc_lib/src/dscresources/dscresource.rs +++ b/dsc_lib/src/dscresources/dscresource.rs @@ -50,8 +50,8 @@ pub enum Capability { Set, /// The resource supports the `_exist` property directly. SetHandlesExist, - /// The resource supports the `what-if` execution type directly. - SetHandlesWhatIf, + /// The resource supports simulating configuration directly. + WhatIf, /// The resource supports validating configuration. Test, /// The resource supports deleting configuration. @@ -211,13 +211,7 @@ impl Invoke for DscResource { return Err(DscError::MissingManifest(self.type_name.clone())); }; let resource_manifest = import_manifest(manifest.clone())?; - let execution = if self.capabilities.contains(&Capability::SetHandlesWhatIf) && execution_type == &ExecutionKind::WhatIfDSC { - ExecutionKind::WhatIfResource - } - else { - execution_type.clone() - }; - command_resource::invoke_set(&resource_manifest, &self.directory, desired, skip_test, &execution) + command_resource::invoke_set(&resource_manifest, &self.directory, desired, skip_test, execution_type) }, } } diff --git a/dsc_lib/src/dscresources/resource_manifest.rs b/dsc_lib/src/dscresources/resource_manifest.rs index fc4b8e1cd..5ba0e6716 100644 --- a/dsc_lib/src/dscresources/resource_manifest.rs +++ b/dsc_lib/src/dscresources/resource_manifest.rs @@ -40,6 +40,9 @@ pub struct ResourceManifest { /// Details how to call the Set method of the resource. #[serde(skip_serializing_if = "Option::is_none")] pub set: Option, + /// Details how to call the `WhatIf` method of the resource. + #[serde(skip_serializing_if = "Option::is_none")] + pub whatif: Option, /// Details how to call the Test method of the resource. #[serde(skip_serializing_if = "Option::is_none")] pub test: Option, @@ -103,11 +106,6 @@ pub enum ArgKind { /// Indicates if argument is mandatory which will pass an empty string if no JSON input is provided. Default is false. mandatory: Option, }, - WhatIf { - /// The argument that serves as the what-if switch. - #[serde(rename = "whatIfSwitchArg")] - what_if_input_arg: String, - } } #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] diff --git a/tools/dsctest/dscwhatif.dsc.resource.json b/tools/dsctest/dscwhatif.dsc.resource.json index c04bfd8b5..a3b9dd658 100644 --- a/tools/dsctest/dscwhatif.dsc.resource.json +++ b/tools/dsctest/dscwhatif.dsc.resource.json @@ -5,16 +5,21 @@ "get": { "executable": "dsctest", "args": [ - "what-if" + "whatif" ] }, "set": { "executable": "dsctest", "args": [ - "what-if", - { - "whatIfSwitchArg": "--what-if" - } + "whatif" + ], + "return": "state" + }, + "whatif": { + "executable": "dsctest", + "args": [ + "whatif", + "-w" ], "return": "state" }, diff --git a/tools/dsctest/src/args.rs b/tools/dsctest/src/args.rs index 5e4283e6a..51b69e83f 100644 --- a/tools/dsctest/src/args.rs +++ b/tools/dsctest/src/args.rs @@ -56,9 +56,9 @@ pub enum SubCommand { #[clap(name = "trace", about = "The trace level")] Trace, - #[clap(name = "what-if", about = "Check if it is a what-if operation")] + #[clap(name = "whatif", about = "Check if it is a whatif operation")] WhatIf { - #[clap(name = "what-if", short, long, help = "Run as a what-if executionType instead of actual executionType")] + #[clap(name = "whatif", short, long, help = "Run as a whatif executionType instead of actual executionType")] what_if: bool, } } From 857c03de94c383410fdf124f4dd516e341eaa563 Mon Sep 17 00:00:00 2001 From: Tess Gauthier Date: Thu, 6 Jun 2024 15:47:01 -0400 Subject: [PATCH 7/7] rename to whatIf --- dsc_lib/src/discovery/command_discovery.rs | 2 +- dsc_lib/src/dscresources/command_resource.rs | 17 ++++++++++------- dsc_lib/src/dscresources/resource_manifest.rs | 4 ++-- tools/dsctest/dscwhatif.dsc.resource.json | 2 +- 4 files changed, 14 insertions(+), 11 deletions(-) diff --git a/dsc_lib/src/discovery/command_discovery.rs b/dsc_lib/src/discovery/command_discovery.rs index 241727ac5..8c431becc 100644 --- a/dsc_lib/src/discovery/command_discovery.rs +++ b/dsc_lib/src/discovery/command_discovery.rs @@ -450,7 +450,7 @@ fn load_manifest(path: &Path) -> Result { capabilities.push(Capability::SetHandlesExist); } } - if manifest.whatif.is_some() { + if manifest.what_if.is_some() { capabilities.push(Capability::WhatIf); } if manifest.test.is_some() { diff --git a/dsc_lib/src/dscresources/command_resource.rs b/dsc_lib/src/dscresources/command_resource.rs index 100ffc648..fd2d997af 100644 --- a/dsc_lib/src/dscresources/command_resource.rs +++ b/dsc_lib/src/dscresources/command_resource.rs @@ -97,21 +97,24 @@ pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_te // TODO: support import resources let operation_type: String; let mut is_synthetic_what_if = false; - let set = match execution_type { + let set_method = match execution_type { ExecutionKind::Actual => { operation_type = "set".to_string(); - resource.set.clone().ok_or(DscError::NotImplemented("set".to_string()))? + &resource.set }, ExecutionKind::WhatIf => { operation_type = "whatif".to_string(); - if let Some(whatif) = &resource.whatif { - whatif.clone() - } else { + if resource.what_if.is_none() { is_synthetic_what_if = true; - resource.set.clone().ok_or(DscError::NotImplemented("set".to_string()))? + &resource.set + } else { + &resource.what_if } } }; + let Some(set) = set_method else { + return Err(DscError::NotImplemented("set".to_string())); + }; verify_json(resource, cwd, desired)?; // if resource doesn't implement a pre-test, we execute test first to see if a set is needed @@ -153,7 +156,7 @@ pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_te let args = process_args(&get.args, desired); let command_input = get_command_input(&get.input, desired)?; - info!("Getting current state for {} by invoking get {} using {}", operation_type, &resource.resource_type, &get.executable); + info!("Getting current state for {} by invoking get '{}' using '{}'", operation_type, &resource.resource_type, &get.executable); let (exit_code, stdout, stderr) = invoke_command(&get.executable, args, command_input.stdin.as_deref(), Some(cwd), command_input.env)?; if resource.kind == Some(Kind::Resource) { diff --git a/dsc_lib/src/dscresources/resource_manifest.rs b/dsc_lib/src/dscresources/resource_manifest.rs index 5ba0e6716..c6aee73c3 100644 --- a/dsc_lib/src/dscresources/resource_manifest.rs +++ b/dsc_lib/src/dscresources/resource_manifest.rs @@ -41,8 +41,8 @@ pub struct ResourceManifest { #[serde(skip_serializing_if = "Option::is_none")] pub set: Option, /// Details how to call the `WhatIf` method of the resource. - #[serde(skip_serializing_if = "Option::is_none")] - pub whatif: Option, + #[serde(rename = "whatIf", skip_serializing_if = "Option::is_none")] + pub what_if: Option, /// Details how to call the Test method of the resource. #[serde(skip_serializing_if = "Option::is_none")] pub test: Option, diff --git a/tools/dsctest/dscwhatif.dsc.resource.json b/tools/dsctest/dscwhatif.dsc.resource.json index a3b9dd658..9d336ee2f 100644 --- a/tools/dsctest/dscwhatif.dsc.resource.json +++ b/tools/dsctest/dscwhatif.dsc.resource.json @@ -15,7 +15,7 @@ ], "return": "state" }, - "whatif": { + "whatIf": { "executable": "dsctest", "args": [ "whatif",