From 79d30ca3c135f95724422ed7ff5749ec8a713ef8 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Fri, 5 Apr 2024 14:16:30 -0700 Subject: [PATCH 1/8] Change how args get passed to commands --- dsc_lib/src/dscresources/command_resource.rs | 117 ++++++++++++------ dsc_lib/src/dscresources/resource_manifest.rs | 40 ++++-- tools/dsctest/dscecho.dsc.resource.json | 33 +++-- 3 files changed, 123 insertions(+), 67 deletions(-) diff --git a/dsc_lib/src/dscresources/command_resource.rs b/dsc_lib/src/dscresources/command_resource.rs index 359a9f43..a40302e9 100644 --- a/dsc_lib/src/dscresources/command_resource.rs +++ b/dsc_lib/src/dscresources/command_resource.rs @@ -3,10 +3,10 @@ use jsonschema::JSONSchema; use serde_json::Value; -use std::{collections::HashMap, env, process::Command, io::{Write, Read}, process::Stdio}; +use std::{collections::HashMap, env, io::{Read, Write}, process::{Command, Stdio}}; use crate::{dscerror::DscError, dscresources::invoke_result::{ResourceGetResponse, ResourceSetResponse, ResourceTestResponse}}; use crate::configure::config_result::ResourceGetResult; -use super::{dscresource::get_diff,resource_manifest::{Kind, ResourceManifest, InputKind, ReturnKind, SchemaKind}, invoke_result::{GetResult, SetResult, TestResult, ValidateResult, ExportResult}}; +use super::{dscresource::get_diff, invoke_result::{ExportResult, GetResult, SetResult, TestResult, ValidateResult}, resource_manifest::{ArgKind, InputKind, Kind, ResourceManifest, ReturnKind, SchemaKind}}; use tracing::{error, warn, info, debug, trace}; pub const EXIT_PROCESS_TERMINATED: i32 = 0x102; @@ -54,7 +54,7 @@ pub fn invoke_get(resource: &ResourceManifest, cwd: &str, filter: &str) -> Resul let mut env: Option> = None; let mut input_filter: Option<&str> = None; - let mut get_args = resource.get.args.clone(); + let mut args: Option> = None; if !filter.is_empty() { verify_json(resource, cwd, filter)?; @@ -65,14 +65,14 @@ pub fn invoke_get(resource: &ResourceManifest, cwd: &str, filter: &str) -> Resul InputKind::Stdin => { input_filter = Some(filter); }, - InputKind::Arg(arg_name) => { - replace_token(&mut get_args, &arg_name, filter)?; + InputKind::Arg => { + args = process_args(&resource.get.args, filter); }, } } info!("Invoking get '{}' using '{}'", &resource.resource_type, &resource.get.executable); - let (exit_code, stdout, stderr) = invoke_command(&resource.get.executable, get_args, input_filter, Some(cwd), env)?; + let (exit_code, stdout, stderr) = invoke_command(&resource.get.executable, args, input_filter, Some(cwd), env)?; log_resource_traces(&stderr); if exit_code != 0 { return Err(DscError::Command(resource.resource_type.clone(), exit_code, stderr)); @@ -114,7 +114,7 @@ pub fn invoke_get(resource: &ResourceManifest, cwd: &str, filter: &str) -> Resul /// Error returned if the resource does not successfully set the desired state #[allow(clippy::too_many_lines)] pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_test: bool) -> Result { - let Some(set) = resource.set.as_ref() else { + let Some(set) = &resource.set else { return Err(DscError::NotImplemented("set".to_string())); }; verify_json(resource, cwd, desired)?; @@ -146,7 +146,7 @@ pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_te let mut get_env: Option> = None; let mut get_input: Option<&str> = None; - let mut get_args = resource.get.args.clone(); + let mut args: Option> = None; match &resource.get.input { Some(InputKind::Env) => { get_env = Some(json_to_hashmap(desired)?); @@ -154,8 +154,8 @@ pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_te Some(InputKind::Stdin) => { get_input = Some(desired); }, - Some(InputKind::Arg(arg_token)) => { - replace_token(&mut get_args, arg_token, desired)?; + Some(InputKind::Arg) => { + args = process_args(&resource.get.args, desired); }, None => { // leave input as none @@ -163,7 +163,7 @@ pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_te } info!("Getting current state for set by invoking get {} using {}", &resource.resource_type, &resource.get.executable); - let (exit_code, stdout, stderr) = invoke_command(&resource.get.executable, get_args, get_input, Some(cwd), get_env)?; + let (exit_code, stdout, stderr) = invoke_command(&resource.get.executable, args, get_input, Some(cwd), get_env)?; log_resource_traces(&stderr); if exit_code != 0 { return Err(DscError::Command(resource.resource_type.clone(), exit_code, stderr)); @@ -183,7 +183,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 = set.args.clone(); + let mut args: Option> = None; match &set.input { InputKind::Env => { env = Some(json_to_hashmap(desired)?); @@ -191,8 +191,8 @@ pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_te InputKind::Stdin => { input_desired = Some(desired); }, - InputKind::Arg(arg_token) => { - replace_token(&mut args, arg_token, desired)?; + InputKind::Arg => { + args = process_args(&set.args, desired); }, } @@ -290,7 +290,7 @@ pub fn invoke_test(resource: &ResourceManifest, cwd: &str, expected: &str) -> Re let mut env: Option> = None; let mut input_expected: Option<&str> = None; - let mut args = test.args.clone(); + let mut args: Option> = None; match &test.input { InputKind::Env => { env = Some(json_to_hashmap(expected)?); @@ -298,8 +298,8 @@ pub fn invoke_test(resource: &ResourceManifest, cwd: &str, expected: &str) -> Re InputKind::Stdin => { input_expected = Some(expected); }, - InputKind::Arg(arg_token) => { - replace_token(&mut args, arg_token, expected)?; + InputKind::Arg => { + args = process_args(&test.args, expected); }, } @@ -401,15 +401,15 @@ fn invoke_synthetic_test(resource: &ResourceManifest, cwd: &str, expected: &str) } /// Invoke the delete operation against a command resource. -/// +/// /// # Arguments -/// +/// /// * `resource` - The resource manifest for the command resource. /// * `cwd` - The current working directory. /// * `filter` - The filter to apply to the resource in JSON. -/// +/// /// # Errors -/// +/// /// Error is returned if the underlying command returns a non-zero exit code. pub fn invoke_delete(resource: &ResourceManifest, cwd: &str, filter: &str) -> Result<(), DscError> { let Some(delete) = &resource.delete else { @@ -418,7 +418,7 @@ pub fn invoke_delete(resource: &ResourceManifest, cwd: &str, filter: &str) -> Re let mut env: Option> = None; let mut input_filter: Option<&str> = None; - let mut delete_args = delete.args.clone(); + let mut args: Option> = None; verify_json(resource, cwd, filter)?; match &delete.input { InputKind::Env => { @@ -427,13 +427,13 @@ pub fn invoke_delete(resource: &ResourceManifest, cwd: &str, filter: &str) -> Re InputKind::Stdin => { input_filter = Some(filter); }, - InputKind::Arg(arg_name) => { - replace_token(&mut delete_args, arg_name, filter)?; + InputKind::Arg => { + args = process_args(&delete.args, filter); }, } info!("Invoking delete '{}' using '{}'", &resource.resource_type, &delete.executable); - let (exit_code, _stdout, stderr) = invoke_command(&delete.executable, delete_args, input_filter, Some(cwd), env)?; + let (exit_code, _stdout, stderr) = invoke_command(&delete.executable, args, input_filter, Some(cwd), env)?; log_resource_traces(&stderr); if exit_code != 0 { return Err(DscError::Command(resource.resource_type.clone(), exit_code, stderr)); @@ -464,7 +464,22 @@ pub fn invoke_validate(resource: &ResourceManifest, cwd: &str, config: &str) -> return Err(DscError::NotImplemented("validate".to_string())); }; - let (exit_code, stdout, stderr) = invoke_command(&validate.executable, validate.args.clone(), Some(config), Some(cwd), None)?; + let mut env: Option> = None; + let mut input_config: Option<&str> = None; + let mut args: Option> = None; + match &validate.input { + InputKind::Env => { + env = Some(json_to_hashmap(config)?); + }, + InputKind::Stdin => { + input_config = Some(config); + }, + InputKind::Arg => { + args = process_args(&validate.args, config); + }, + } + + let (exit_code, stdout, stderr) = invoke_command(&validate.executable, args, input_config, Some(cwd), env)?; log_resource_traces(&stderr); if exit_code != 0 { return Err(DscError::Command(resource.resource_type.clone(), exit_code, stderr)); @@ -536,7 +551,27 @@ pub fn invoke_export(resource: &ResourceManifest, cwd: &str, input: Option<&str> return Err(DscError::Operation(format!("Export is not supported by resource {}", &resource.resource_type))) }; - let (exit_code, stdout, stderr) = invoke_command(&export.executable, export.args.clone(), input, Some(cwd), None)?; + let mut env: Option> = None; + let mut export_input: Option<&str> = None; + let mut args: Option> = None; + if let Some(input) = input { + match &export.input { + Some(InputKind::Env) => { + env = Some(json_to_hashmap(input)?); + }, + Some(InputKind::Stdin) => { + export_input = Some(input); + }, + Some(InputKind::Arg) => { + args = process_args(&export.args, input); + }, + None => { + // leave input as none + }, + } + } + + let (exit_code, stdout, stderr) = invoke_command(&export.executable, args, export_input, Some(cwd), env)?; log_resource_traces(&stderr); if exit_code != 0 { return Err(DscError::Command(resource.resource_type.clone(), exit_code, stderr)); @@ -634,24 +669,30 @@ pub fn invoke_command(executable: &str, args: Option>, input: Option Ok((exit_code, stdout, stderr)) } -fn replace_token(args: &mut Option>, token: &str, value: &str) -> Result<(), DscError> { +fn process_args(args: &Option>, value: &str) -> Option> { let Some(arg_values) = args else { - return Err(DscError::Operation("No args to replace".to_string())); + debug!("No args to process"); + return None; }; - let mut found = false; + let mut processed_args = Vec::::new(); for arg in arg_values { - if arg == token { - found = true; - *arg = value.to_string(); - } - } + match arg { + ArgKind::String(s) => { + processed_args.push(s.clone()); + }, + ArgKind::Json { json_input_arg, mandatory } => { + if value.is_empty() && *mandatory == Some(true) { + continue; + } - if !found { - return Err(DscError::Operation(format!("Token {token} not found in args"))); + processed_args.push(json_input_arg.clone()); + processed_args.push(value.to_string()); + }, + } } - Ok(()) + Some(processed_args) } fn verify_json(resource: &ResourceManifest, cwd: &str, json: &str) -> Result<(), DscError> { diff --git a/dsc_lib/src/dscresources/resource_manifest.rs b/dsc_lib/src/dscresources/resource_manifest.rs index 3b00a5c1..46e99bca 100644 --- a/dsc_lib/src/dscresources/resource_manifest.rs +++ b/dsc_lib/src/dscresources/resource_manifest.rs @@ -15,7 +15,6 @@ pub enum Kind { Resource, } - #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] #[serde(deny_unknown_fields)] pub struct ResourceManifest { @@ -80,11 +79,26 @@ pub enum ManifestSchemaUri { VSCode2023_08, } +#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] +#[serde(untagged)] +pub enum ArgKind { + /// The argument is a string. + String(String), + /// The argument accepts the JSON input object. + Json{ + /// The argument that accepts the JSON input object. + #[serde(rename = "jsonInputArg")] + json_input_arg: String, + /// Indicates if argument is mandatory which will pass an empty string if no JSON input is provided. Default is false. + mandatory: Option, + } +} + #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] pub enum InputKind { /// The input replaces arguments with this token in the command. #[serde(rename = "arg")] - Arg(String), + Arg, /// The input is accepted as environmental variables. #[serde(rename = "env")] Env, @@ -129,7 +143,7 @@ pub struct GetMethod { /// The command to run to get the state of the resource. pub executable: String, /// The arguments to pass to the command to perform a Get. - pub args: Option>, + pub args: Option>, /// How to pass optional input for a Get. #[serde(skip_serializing_if = "Option::is_none")] pub input: Option, @@ -140,7 +154,7 @@ pub struct SetMethod { /// The command to run to set the state of the resource. pub executable: String, /// The arguments to pass to the command to perform a Set. - pub args: Option>, + pub args: Option>, /// How to pass required input for a Set. pub input: InputKind, /// Whether to run the Test method before the Set method. True means the resource will perform its own test before running the Set method. @@ -159,7 +173,7 @@ pub struct TestMethod { /// The command to run to test the state of the resource. pub executable: String, /// The arguments to pass to the command to perform a Test. - pub args: Option>, + pub args: Option>, /// How to pass required input for a Test. pub input: InputKind, /// The type of return value expected from the Test method. @@ -169,11 +183,11 @@ pub struct TestMethod { #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] pub struct DeleteMethod { - /// The command to run to test the state of the resource. + /// The command to run to delete the state of the resource. pub executable: String, - /// The arguments to pass to the command to perform a Test. - pub args: Option>, - /// How to pass required input for a Test. + /// The arguments to pass to the command to perform a Delete. + pub args: Option>, + /// How to pass required input for a Delete. pub input: InputKind, } @@ -182,7 +196,9 @@ pub struct ValidateMethod { // TODO: enable validation via schema or command /// The command to run to validate the state of the resource. pub executable: String, /// The arguments to pass to the command to perform a Validate. - pub args: Option>, + pub args: Option>, + /// How to pass required input for a Validate. + pub input: InputKind, } #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] @@ -190,7 +206,9 @@ pub struct ExportMethod { /// The command to run to enumerate instances of the resource. pub executable: String, /// The arguments to pass to the command to perform a Export. - pub args: Option>, + pub args: Option>, + /// How to pass input for a Export. + pub input: Option, } #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] diff --git a/tools/dsctest/dscecho.dsc.resource.json b/tools/dsctest/dscecho.dsc.resource.json index 2390934d..ae0069da 100644 --- a/tools/dsctest/dscecho.dsc.resource.json +++ b/tools/dsctest/dscecho.dsc.resource.json @@ -6,34 +6,31 @@ "executable": "dsctest", "args": [ "echo", - "--input", - "{json}" - ], - "input": { - "arg": "{json}" - } + { + "jsonInputArg": "--input", + "mandatory": true + } + ] }, "set": { "executable": "dsctest", "args": [ "echo", - "--input", - "{json}" - ], - "input": { - "arg": "{json}" - } + { + "jsonInputArg": "--input", + "mandatory": true + } + ] }, "test": { "executable": "dsctest", "args": [ "echo", - "--input", - "{json}" - ], - "input": { - "arg": "{json}" - } + { + "jsonInputArg": "--input", + "mandatory": true + } + ] }, "schema": { "command": { From 32076fbb502087b4db405bb67e7789d48b309ea2 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Fri, 5 Apr 2024 15:14:47 -0700 Subject: [PATCH 2/8] fix resource manifests --- dsc/assertion.dsc.resource.json | 3 +- dsc/group.dsc.resource.json | 3 +- dsc/parallel.dsc.resource.json | 3 +- dsc/tests/dsc_args.tests.ps1 | 14 ++- dsc_lib/src/dscresources/command_resource.rs | 88 +++++++++---------- dsc_lib/src/dscresources/resource_manifest.rs | 11 +-- .../powershell.dsc.resource.json | 3 +- registry/registry.dsc.resource.json | 35 ++++---- tools/dsctest/dscexist.dsc.resource.json | 20 ++--- tools/dsctest/dscsleep.dsc.resource.json | 33 ++++--- 10 files changed, 101 insertions(+), 112 deletions(-) diff --git a/dsc/assertion.dsc.resource.json b/dsc/assertion.dsc.resource.json index 9cfb090e..4aa67db4 100644 --- a/dsc/assertion.dsc.resource.json +++ b/dsc/assertion.dsc.resource.json @@ -60,6 +60,7 @@ "args": [ "config", "validate" - ] + ], + "input": "stdin" } } diff --git a/dsc/group.dsc.resource.json b/dsc/group.dsc.resource.json index 626176cb..8eccaa9a 100644 --- a/dsc/group.dsc.resource.json +++ b/dsc/group.dsc.resource.json @@ -48,6 +48,7 @@ "args": [ "config", "validate" - ] + ], + "input": "stdin" } } diff --git a/dsc/parallel.dsc.resource.json b/dsc/parallel.dsc.resource.json index 083a7f5b..79f081aa 100644 --- a/dsc/parallel.dsc.resource.json +++ b/dsc/parallel.dsc.resource.json @@ -51,6 +51,7 @@ "args": [ "config", "validate" - ] + ], + "input": "stdin" } } diff --git a/dsc/tests/dsc_args.tests.ps1 b/dsc/tests/dsc_args.tests.ps1 index 25a2df09..6a7e48ae 100644 --- a/dsc/tests/dsc_args.tests.ps1 +++ b/dsc/tests/dsc_args.tests.ps1 @@ -54,24 +54,20 @@ Describe 'config argument tests' { $env:DSC_RESOURCE_PATH = $oldPath } - It 'input is ' -Skip:(!$IsWindows) -TestCases @( + It 'input is ' -TestCases @( @{ type = 'yaml'; text = @' - keyPath: HKLM\Software\Microsoft\Windows NT\CurrentVersion - valueName: ProductName + output: Hello There '@ } @{ type = 'json'; text = @' { - "keyPath": "HKLM\\Software\\Microsoft\\Windows NT\\CurrentVersion", - "valueName": "ProductName" + "output": "Hello There" } '@ } ) { param($text) - $output = $text | dsc resource get -r Microsoft.Windows/Registry + $output = $text | dsc resource get -r Test/Echo $output = $output | ConvertFrom-Json - $output.actualState.keyPath | Should -BeExactly 'HKLM\Software\Microsoft\Windows NT\CurrentVersion' - $output.actualState.valueName | Should -BeExactly 'ProductName' - $output.actualState.valueData.String | Should -Match 'Windows .*' + $output.actualState.output | Should -BeExactly 'Hello There' } It '--format is used even when redirected' -TestCases @( diff --git a/dsc_lib/src/dscresources/command_resource.rs b/dsc_lib/src/dscresources/command_resource.rs index a40302e9..e52f2743 100644 --- a/dsc_lib/src/dscresources/command_resource.rs +++ b/dsc_lib/src/dscresources/command_resource.rs @@ -54,7 +54,7 @@ pub fn invoke_get(resource: &ResourceManifest, cwd: &str, filter: &str) -> Resul let mut env: Option> = None; let mut input_filter: Option<&str> = None; - let mut args: Option> = None; + let args = process_args(&resource.get.args, filter); if !filter.is_empty() { verify_json(resource, cwd, filter)?; @@ -65,9 +65,6 @@ pub fn invoke_get(resource: &ResourceManifest, cwd: &str, filter: &str) -> Resul InputKind::Stdin => { input_filter = Some(filter); }, - InputKind::Arg => { - args = process_args(&resource.get.args, filter); - }, } } @@ -146,7 +143,7 @@ pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_te let mut get_env: Option> = None; let mut get_input: Option<&str> = None; - let mut args: Option> = None; + let args = process_args(&resource.get.args, desired); match &resource.get.input { Some(InputKind::Env) => { get_env = Some(json_to_hashmap(desired)?); @@ -154,9 +151,6 @@ pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_te Some(InputKind::Stdin) => { get_input = Some(desired); }, - Some(InputKind::Arg) => { - args = process_args(&resource.get.args, desired); - }, None => { // leave input as none }, @@ -183,16 +177,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 mut args: Option> = None; + let args = process_args(&set.args, desired); match &set.input { - InputKind::Env => { + Some(InputKind::Env) => { env = Some(json_to_hashmap(desired)?); }, - InputKind::Stdin => { + Some(InputKind::Stdin) => { input_desired = Some(desired); }, - InputKind::Arg => { - args = process_args(&set.args, desired); + None => { + // leave input as none }, } @@ -290,16 +284,16 @@ pub fn invoke_test(resource: &ResourceManifest, cwd: &str, expected: &str) -> Re let mut env: Option> = None; let mut input_expected: Option<&str> = None; - let mut args: Option> = None; + let args = process_args(&test.args, expected); match &test.input { - InputKind::Env => { + Some(InputKind::Env) => { env = Some(json_to_hashmap(expected)?); }, - InputKind::Stdin => { + Some(InputKind::Stdin) => { input_expected = Some(expected); }, - InputKind::Arg => { - args = process_args(&test.args, expected); + None => { + // leave input as none }, } @@ -416,19 +410,20 @@ pub fn invoke_delete(resource: &ResourceManifest, cwd: &str, filter: &str) -> Re return Err(DscError::NotImplemented("delete".to_string())); }; + verify_json(resource, cwd, filter)?; + let mut env: Option> = None; let mut input_filter: Option<&str> = None; - let mut args: Option> = None; - verify_json(resource, cwd, filter)?; + let args = process_args(&delete.args, filter); match &delete.input { - InputKind::Env => { + Some(InputKind::Env) => { env = Some(json_to_hashmap(filter)?); }, - InputKind::Stdin => { + Some(InputKind::Stdin) => { input_filter = Some(filter); }, - InputKind::Arg => { - args = process_args(&delete.args, filter); + None => { + // leave input as none }, } @@ -466,16 +461,16 @@ pub fn invoke_validate(resource: &ResourceManifest, cwd: &str, config: &str) -> let mut env: Option> = None; let mut input_config: Option<&str> = None; - let mut args: Option> = None; + let args = process_args(&validate.args, config); match &validate.input { - InputKind::Env => { + Some(InputKind::Env) => { env = Some(json_to_hashmap(config)?); }, - InputKind::Stdin => { + Some(InputKind::Stdin) => { input_config = Some(config); }, - InputKind::Arg => { - args = process_args(&validate.args, config); + None => { + // leave input as none }, } @@ -551,24 +546,29 @@ pub fn invoke_export(resource: &ResourceManifest, cwd: &str, input: Option<&str> return Err(DscError::Operation(format!("Export is not supported by resource {}", &resource.resource_type))) }; + let mut env: Option> = None; let mut export_input: Option<&str> = None; - let mut args: Option> = None; + let args: Option>; if let Some(input) = input { - match &export.input { - Some(InputKind::Env) => { - env = Some(json_to_hashmap(input)?); - }, - Some(InputKind::Stdin) => { - export_input = Some(input); - }, - Some(InputKind::Arg) => { - args = process_args(&export.args, input); - }, - None => { - // leave input as none - }, + if !input.is_empty() { + verify_json(resource, cwd, input)?; + match &export.input { + Some(InputKind::Env) => { + env = Some(json_to_hashmap(input)?); + }, + Some(InputKind::Stdin) => { + export_input = Some(input); + }, + None => { + // leave input as none + }, + } } + + args = process_args(&export.args, input); + } else { + args = process_args(&export.args, ""); } let (exit_code, stdout, stderr) = invoke_command(&export.executable, args, export_input, Some(cwd), env)?; @@ -682,7 +682,7 @@ fn process_args(args: &Option>, value: &str) -> Option> processed_args.push(s.clone()); }, ArgKind::Json { json_input_arg, mandatory } => { - if value.is_empty() && *mandatory == Some(true) { + if value.is_empty() && *mandatory != Some(true) { continue; } diff --git a/dsc_lib/src/dscresources/resource_manifest.rs b/dsc_lib/src/dscresources/resource_manifest.rs index 46e99bca..fe75ca84 100644 --- a/dsc_lib/src/dscresources/resource_manifest.rs +++ b/dsc_lib/src/dscresources/resource_manifest.rs @@ -96,9 +96,6 @@ pub enum ArgKind { #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] pub enum InputKind { - /// The input replaces arguments with this token in the command. - #[serde(rename = "arg")] - Arg, /// The input is accepted as environmental variables. #[serde(rename = "env")] Env, @@ -156,7 +153,7 @@ pub struct SetMethod { /// The arguments to pass to the command to perform a Set. pub args: Option>, /// How to pass required input for a Set. - pub input: InputKind, + pub input: Option, /// Whether to run the Test method before the Set method. True means the resource will perform its own test before running the Set method. #[serde(rename = "implementsPretest", skip_serializing_if = "Option::is_none")] pub pre_test: Option, @@ -175,7 +172,7 @@ pub struct TestMethod { /// The arguments to pass to the command to perform a Test. pub args: Option>, /// How to pass required input for a Test. - pub input: InputKind, + pub input: Option, /// The type of return value expected from the Test method. #[serde(rename = "return", skip_serializing_if = "Option::is_none")] pub returns: Option, @@ -188,7 +185,7 @@ pub struct DeleteMethod { /// The arguments to pass to the command to perform a Delete. pub args: Option>, /// How to pass required input for a Delete. - pub input: InputKind, + pub input: Option, } #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] @@ -198,7 +195,7 @@ pub struct ValidateMethod { // TODO: enable validation via schema or command /// The arguments to pass to the command to perform a Validate. pub args: Option>, /// How to pass required input for a Validate. - pub input: InputKind, + pub input: Option, } #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)] diff --git a/powershell-adapter/powershell.dsc.resource.json b/powershell-adapter/powershell.dsc.resource.json index 76c00eb1..3691aa8f 100644 --- a/powershell-adapter/powershell.dsc.resource.json +++ b/powershell-adapter/powershell.dsc.resource.json @@ -76,7 +76,8 @@ "-NoProfile", "-Command", "$Input | ./powershell.resource.ps1 Validate" - ] + ], + "input": "stdin" }, "exitCodes": { "0": "Success", diff --git a/registry/registry.dsc.resource.json b/registry/registry.dsc.resource.json index 21aa5d97..8c2a2cf7 100644 --- a/registry/registry.dsc.resource.json +++ b/registry/registry.dsc.resource.json @@ -11,36 +11,33 @@ "args": [ "config", "get", - "--input", - "{json}" - ], - "input": { - "arg": "{json}" - } + { + "jsonInputArg": "--input", + "mandatory": true + } + ] }, "set": { "executable": "registry", "args": [ "config", "set", - "--input", - "{json}" - ], - "input": { - "arg": "{json}" - } + { + "jsonInputArg": "--input", + "mandatory": true + } + ] }, "delete": { "executable": "registry", "args": [ "config", - "delete", - "--input", - "{json}" - ], - "input": { - "arg": "{json}" - } + "remove", + { + "jsonInputArg": "--input", + "mandatory": true + } + ] }, "exitCodes": { "0": "Success", diff --git a/tools/dsctest/dscexist.dsc.resource.json b/tools/dsctest/dscexist.dsc.resource.json index 0f909fd1..50b0ac1e 100644 --- a/tools/dsctest/dscexist.dsc.resource.json +++ b/tools/dsctest/dscexist.dsc.resource.json @@ -6,23 +6,21 @@ "executable": "dsctest", "args": [ "exist", - "--input", - "{json}" - ], - "input": { - "arg": "{json}" - } + { + "jsonInputArg": "--input", + "mandatory": true + } + ] }, "set": { "executable": "dsctest", "args": [ "exist", - "--input", - "{json}" + { + "jsonInputArg": "--input", + "mandatory": true + } ], - "input": { - "arg": "{json}" - }, "handlesExist": true, "return": "state" }, diff --git a/tools/dsctest/dscsleep.dsc.resource.json b/tools/dsctest/dscsleep.dsc.resource.json index 6d8ce7fd..bc5d61df 100644 --- a/tools/dsctest/dscsleep.dsc.resource.json +++ b/tools/dsctest/dscsleep.dsc.resource.json @@ -6,34 +6,31 @@ "executable": "dsctest", "args": [ "sleep", - "--input", - "{json}" - ], - "input": { - "arg": "{json}" - } + { + "jsonInputArg": "--input", + "mandatory": true + } + ] }, "set": { "executable": "dsctest", "args": [ "sleep", - "--input", - "{json}" - ], - "input": { - "arg": "{json}" - } + { + "jsonInputArg": "--input", + "mandatory": true + } + ] }, "test": { "executable": "dsctest", "args": [ "sleep", - "--input", - "{json}" - ], - "input": { - "arg": "{json}" - } + { + "jsonInputArg": "--input", + "mandatory": true + } + ] }, "schema": { "command": { From acf9032f94806c5fa97ed184df54538b892114f9 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Fri, 5 Apr 2024 16:19:29 -0700 Subject: [PATCH 3/8] fix manifest for Test/Delete resource --- tools/dsctest/dscdelete.dsc.resource.json | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/tools/dsctest/dscdelete.dsc.resource.json b/tools/dsctest/dscdelete.dsc.resource.json index 188522fa..317bc7c8 100644 --- a/tools/dsctest/dscdelete.dsc.resource.json +++ b/tools/dsctest/dscdelete.dsc.resource.json @@ -6,23 +6,21 @@ "executable": "dsctest", "args": [ "delete", - "--input", - "{json}" - ], - "input": { - "arg": "{json}" - } + { + "jsonInputArg": "--input", + "mandatory": true + } + ] }, "delete": { "executable": "dsctest", "args": [ "delete", - "--input", - "{json}" - ], - "input": { - "arg": "{json}" - } + { + "jsonInputArg": "--input", + "mandatory": true + } + ] }, "schema": { "command": { From fdab2f18674ed98ff2cfa70a92d3418096cb575b Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Fri, 5 Apr 2024 17:14:33 -0700 Subject: [PATCH 4/8] fix registry resource --- registry/registry.dsc.resource.json | 2 +- registry/src/args.rs | 2 +- registry/src/main.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/registry/registry.dsc.resource.json b/registry/registry.dsc.resource.json index 8c2a2cf7..b989ba94 100644 --- a/registry/registry.dsc.resource.json +++ b/registry/registry.dsc.resource.json @@ -32,7 +32,7 @@ "executable": "registry", "args": [ "config", - "remove", + "delete", { "jsonInputArg": "--input", "mandatory": true diff --git a/registry/src/args.rs b/registry/src/args.rs index 889fdaa7..7c0302cf 100644 --- a/registry/src/args.rs +++ b/registry/src/args.rs @@ -24,7 +24,7 @@ pub enum ConfigSubCommand { input: String, }, #[clap(name = "delete", about = "Delete registry configuration.")] - Remove { + Delete { #[clap(short, long, required = true, help = "The registry JSON input.")] input: String, }, diff --git a/registry/src/main.rs b/registry/src/main.rs index 1581740b..f0d6826d 100644 --- a/registry/src/main.rs +++ b/registry/src/main.rs @@ -79,7 +79,7 @@ fn main() { } } }, - args::ConfigSubCommand::Remove{input} => { + args::ConfigSubCommand::Delete{input} => { let reg_helper = match RegistryHelper::new(&input) { Ok(reg_helper) => reg_helper, Err(err) => { From aeabe241e2f7efb4246abb7eb9f480c29230e7d8 Mon Sep 17 00:00:00 2001 From: "Steve Lee (POWERSHELL HE/HIM) (from Dev Box)" Date: Wed, 10 Apr 2024 13:00:21 -0700 Subject: [PATCH 5/8] update test --- dsc/tests/dsc_discovery.tests.ps1 | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/dsc/tests/dsc_discovery.tests.ps1 b/dsc/tests/dsc_discovery.tests.ps1 index 6e4c49af..732e3a25 100644 --- a/dsc/tests/dsc_discovery.tests.ps1 +++ b/dsc/tests/dsc_discovery.tests.ps1 @@ -77,13 +77,8 @@ Describe 'tests for resource discovery' { "get": { "executable": "dsctest", "args": [ - "echo", - "--input", - "{json}" - ], - "input": { - "arg": "{json}" - } + "echo" + ] }, "schema": { "command": { From 6db080b432062715ba09fc0be63f0099f5bce779 Mon Sep 17 00:00:00 2001 From: "Steve Lee (POWERSHELL HE/HIM) (from Dev Box)" Date: Fri, 12 Apr 2024 16:55:11 -0700 Subject: [PATCH 6/8] address Andrew and Tess' feedback --- dsc/tests/dsc_discovery.tests.ps1 | 15 +-- dsc_lib/src/dscresources/command_resource.rs | 99 ++++++-------------- 2 files changed, 34 insertions(+), 80 deletions(-) diff --git a/dsc/tests/dsc_discovery.tests.ps1 b/dsc/tests/dsc_discovery.tests.ps1 index 732e3a25..b95cd743 100644 --- a/dsc/tests/dsc_discovery.tests.ps1 +++ b/dsc/tests/dsc_discovery.tests.ps1 @@ -72,22 +72,14 @@ Describe 'tests for resource discovery' { $manifest = @' { "$schema": "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/bundled/resource/manifest.json", - "type": "Test/Echo", + "type": "Test/InvalidSemver", "version": "1.1.0..1", "get": { - "executable": "dsctest", - "args": [ - "echo" - ] + "executable": "dsctest" }, "schema": { "command": { - "executable": "dsctest", - "args": [ - "schema", - "-s", - "echo" - ] + "executable": "dsctest" } } } @@ -97,6 +89,7 @@ Describe 'tests for resource discovery' { $env:DSC_RESOURCE_PATH = $testdrive Set-Content -Path "$testdrive/test.dsc.resource.json" -Value $manifest $out = dsc resource list 2>&1 + write-verbose -verbose ($out | Out-String) $out | Should -Match 'WARN.*?Validation.*?Invalid manifest.*?version' } finally { diff --git a/dsc_lib/src/dscresources/command_resource.rs b/dsc_lib/src/dscresources/command_resource.rs index e52f2743..a559b692 100644 --- a/dsc_lib/src/dscresources/command_resource.rs +++ b/dsc_lib/src/dscresources/command_resource.rs @@ -141,23 +141,11 @@ pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_te } } - let mut get_env: Option> = None; - let mut get_input: Option<&str> = None; let args = process_args(&resource.get.args, desired); - match &resource.get.input { - Some(InputKind::Env) => { - get_env = Some(json_to_hashmap(desired)?); - }, - Some(InputKind::Stdin) => { - get_input = Some(desired); - }, - None => { - // leave input as none - }, - } + let (get_env, get_input) = get_env_stdin(&resource.get.input, desired)?; info!("Getting current state for set by invoking get {} using {}", &resource.resource_type, &resource.get.executable); - let (exit_code, stdout, stderr) = invoke_command(&resource.get.executable, args, get_input, Some(cwd), get_env)?; + let (exit_code, stdout, stderr) = invoke_command(&resource.get.executable, args, get_input.as_deref(), Some(cwd), get_env)?; log_resource_traces(&stderr); if exit_code != 0 { return Err(DscError::Command(resource.resource_type.clone(), exit_code, stderr)); @@ -282,23 +270,11 @@ pub fn invoke_test(resource: &ResourceManifest, cwd: &str, expected: &str) -> Re verify_json(resource, cwd, expected)?; - let mut env: Option> = None; - let mut input_expected: Option<&str> = None; let args = process_args(&test.args, expected); - match &test.input { - Some(InputKind::Env) => { - env = Some(json_to_hashmap(expected)?); - }, - Some(InputKind::Stdin) => { - input_expected = Some(expected); - }, - None => { - // leave input as none - }, - } + let (env, input_expected) = get_env_stdin(&test.input, expected)?; info!("Invoking test '{}' using '{}'", &resource.resource_type, &test.executable); - let (exit_code, stdout, stderr) = invoke_command(&test.executable, args, input_expected, Some(cwd), env)?; + let (exit_code, stdout, stderr) = invoke_command(&test.executable, args, input_expected.as_deref(), Some(cwd), env)?; log_resource_traces(&stderr); if exit_code != 0 { return Err(DscError::Command(resource.resource_type.clone(), exit_code, stderr)); @@ -412,23 +388,11 @@ pub fn invoke_delete(resource: &ResourceManifest, cwd: &str, filter: &str) -> Re verify_json(resource, cwd, filter)?; - let mut env: Option> = None; - let mut input_filter: Option<&str> = None; let args = process_args(&delete.args, filter); - match &delete.input { - Some(InputKind::Env) => { - env = Some(json_to_hashmap(filter)?); - }, - Some(InputKind::Stdin) => { - input_filter = Some(filter); - }, - None => { - // leave input as none - }, - } + let (env, input_filter) = get_env_stdin(&delete.input, filter)?; info!("Invoking delete '{}' using '{}'", &resource.resource_type, &delete.executable); - let (exit_code, _stdout, stderr) = invoke_command(&delete.executable, args, input_filter, Some(cwd), env)?; + let (exit_code, _stdout, stderr) = invoke_command(&delete.executable, args, input_filter.as_deref(), Some(cwd), env)?; log_resource_traces(&stderr); if exit_code != 0 { return Err(DscError::Command(resource.resource_type.clone(), exit_code, stderr)); @@ -459,22 +423,11 @@ pub fn invoke_validate(resource: &ResourceManifest, cwd: &str, config: &str) -> return Err(DscError::NotImplemented("validate".to_string())); }; - let mut env: Option> = None; - let mut input_config: Option<&str> = None; let args = process_args(&validate.args, config); - match &validate.input { - Some(InputKind::Env) => { - env = Some(json_to_hashmap(config)?); - }, - Some(InputKind::Stdin) => { - input_config = Some(config); - }, - None => { - // leave input as none - }, - } + let (env, input_config) = get_env_stdin(&validate.input, config)?; - let (exit_code, stdout, stderr) = invoke_command(&validate.executable, args, input_config, Some(cwd), env)?; + info!("Invoking validate '{}' using '{}'", &resource.resource_type, &validate.executable); + let (exit_code, stdout, stderr) = invoke_command(&validate.executable, args, input_config.as_deref(), Some(cwd), env)?; log_resource_traces(&stderr); if exit_code != 0 { return Err(DscError::Command(resource.resource_type.clone(), exit_code, stderr)); @@ -548,22 +501,12 @@ pub fn invoke_export(resource: &ResourceManifest, cwd: &str, input: Option<&str> let mut env: Option> = None; - let mut export_input: Option<&str> = None; + let mut export_input: Option = None; let args: Option>; if let Some(input) = input { if !input.is_empty() { verify_json(resource, cwd, input)?; - match &export.input { - Some(InputKind::Env) => { - env = Some(json_to_hashmap(input)?); - }, - Some(InputKind::Stdin) => { - export_input = Some(input); - }, - None => { - // leave input as none - }, - } + (env, export_input) = get_env_stdin(&export.input, input)?; } args = process_args(&export.args, input); @@ -571,7 +514,7 @@ pub fn invoke_export(resource: &ResourceManifest, cwd: &str, input: Option<&str> args = process_args(&export.args, ""); } - let (exit_code, stdout, stderr) = invoke_command(&export.executable, args, export_input, Some(cwd), env)?; + let (exit_code, stdout, stderr) = invoke_command(&export.executable, args, export_input.as_deref(), Some(cwd), env)?; log_resource_traces(&stderr); if exit_code != 0 { return Err(DscError::Command(resource.resource_type.clone(), exit_code, stderr)); @@ -695,6 +638,24 @@ fn process_args(args: &Option>, value: &str) -> Option> Some(processed_args) } +fn get_env_stdin(input_kind: &Option, input: &str) -> Result<(Option>, Option), DscError> { + let mut env: Option> = None; + let mut input_value: Option = None; + match input_kind { + Some(InputKind::Env) => { + env = Some(json_to_hashmap(input)?); + }, + Some(InputKind::Stdin) => { + input_value = Some(input.to_string()); + }, + None => { + // leave input as none + }, + } + + Ok((env, input_value)) +} + fn verify_json(resource: &ResourceManifest, cwd: &str, json: &str) -> Result<(), DscError> { debug!("Verify JSON for '{}'", resource.resource_type); From ba80ec716582246496756093d2f0e5e893c2e11c Mon Sep 17 00:00:00 2001 From: "Steve Lee (POWERSHELL HE/HIM) (from Dev Box)" Date: Fri, 12 Apr 2024 17:09:28 -0700 Subject: [PATCH 7/8] fix clippy --- dsc_lib/src/dscresources/command_resource.rs | 61 +++++++++----------- 1 file changed, 27 insertions(+), 34 deletions(-) diff --git a/dsc_lib/src/dscresources/command_resource.rs b/dsc_lib/src/dscresources/command_resource.rs index a559b692..676a1252 100644 --- a/dsc_lib/src/dscresources/command_resource.rs +++ b/dsc_lib/src/dscresources/command_resource.rs @@ -45,31 +45,16 @@ pub fn log_resource_traces(stderr: &str) /// /// Error returned if the resource does not successfully get the current state pub fn invoke_get(resource: &ResourceManifest, cwd: &str, filter: &str) -> Result { - let input_kind = if let Some(input_kind) = &resource.get.input { - input_kind.clone() - } - else { - InputKind::Stdin - }; - - let mut env: Option> = None; - let mut input_filter: Option<&str> = None; + let mut command_input = CommandInput { env: None, stdin: None }; let args = process_args(&resource.get.args, filter); if !filter.is_empty() { verify_json(resource, cwd, filter)?; - match input_kind { - InputKind::Env => { - env = Some(json_to_hashmap(filter)?); - }, - InputKind::Stdin => { - input_filter = Some(filter); - }, - } + command_input = get_command_input(&resource.get.input, filter)?; } info!("Invoking get '{}' using '{}'", &resource.resource_type, &resource.get.executable); - let (exit_code, stdout, stderr) = invoke_command(&resource.get.executable, args, input_filter, Some(cwd), env)?; + let (exit_code, stdout, stderr) = invoke_command(&resource.get.executable, args, command_input.stdin.as_deref(), Some(cwd), command_input.env)?; log_resource_traces(&stderr); if exit_code != 0 { return Err(DscError::Command(resource.resource_type.clone(), exit_code, stderr)); @@ -142,10 +127,10 @@ pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_te } let args = process_args(&resource.get.args, desired); - let (get_env, get_input) = get_env_stdin(&resource.get.input, desired)?; + let command_input = get_command_input(&resource.get.input, desired)?; info!("Getting current state for set by invoking get {} using {}", &resource.resource_type, &resource.get.executable); - let (exit_code, stdout, stderr) = invoke_command(&resource.get.executable, args, get_input.as_deref(), Some(cwd), get_env)?; + let (exit_code, stdout, stderr) = invoke_command(&resource.get.executable, args, command_input.stdin.as_deref(), Some(cwd), command_input.env)?; log_resource_traces(&stderr); if exit_code != 0 { return Err(DscError::Command(resource.resource_type.clone(), exit_code, stderr)); @@ -271,10 +256,10 @@ pub fn invoke_test(resource: &ResourceManifest, cwd: &str, expected: &str) -> Re verify_json(resource, cwd, expected)?; let args = process_args(&test.args, expected); - let (env, input_expected) = get_env_stdin(&test.input, expected)?; + let command_input = get_command_input(&test.input, expected)?; info!("Invoking test '{}' using '{}'", &resource.resource_type, &test.executable); - let (exit_code, stdout, stderr) = invoke_command(&test.executable, args, input_expected.as_deref(), Some(cwd), env)?; + let (exit_code, stdout, stderr) = invoke_command(&test.executable, args, command_input.stdin.as_deref(), Some(cwd), command_input.env)?; log_resource_traces(&stderr); if exit_code != 0 { return Err(DscError::Command(resource.resource_type.clone(), exit_code, stderr)); @@ -389,10 +374,10 @@ pub fn invoke_delete(resource: &ResourceManifest, cwd: &str, filter: &str) -> Re verify_json(resource, cwd, filter)?; let args = process_args(&delete.args, filter); - let (env, input_filter) = get_env_stdin(&delete.input, filter)?; + let command_input = get_command_input(&delete.input, filter)?; info!("Invoking delete '{}' using '{}'", &resource.resource_type, &delete.executable); - let (exit_code, _stdout, stderr) = invoke_command(&delete.executable, args, input_filter.as_deref(), Some(cwd), env)?; + let (exit_code, _stdout, stderr) = invoke_command(&delete.executable, args, command_input.stdin.as_deref(), Some(cwd), command_input.env)?; log_resource_traces(&stderr); if exit_code != 0 { return Err(DscError::Command(resource.resource_type.clone(), exit_code, stderr)); @@ -424,10 +409,10 @@ pub fn invoke_validate(resource: &ResourceManifest, cwd: &str, config: &str) -> }; let args = process_args(&validate.args, config); - let (env, input_config) = get_env_stdin(&validate.input, config)?; + let command_input = get_command_input(&validate.input, config)?; info!("Invoking validate '{}' using '{}'", &resource.resource_type, &validate.executable); - let (exit_code, stdout, stderr) = invoke_command(&validate.executable, args, input_config.as_deref(), Some(cwd), env)?; + let (exit_code, stdout, stderr) = invoke_command(&validate.executable, args, command_input.stdin.as_deref(), Some(cwd), command_input.env)?; log_resource_traces(&stderr); if exit_code != 0 { return Err(DscError::Command(resource.resource_type.clone(), exit_code, stderr)); @@ -500,13 +485,13 @@ pub fn invoke_export(resource: &ResourceManifest, cwd: &str, input: Option<&str> }; - let mut env: Option> = None; - let mut export_input: Option = None; + let mut command_input: CommandInput = CommandInput { env: None, stdin: None }; let args: Option>; if let Some(input) = input { if !input.is_empty() { verify_json(resource, cwd, input)?; - (env, export_input) = get_env_stdin(&export.input, input)?; + + command_input = get_command_input(&export.input, input)?; } args = process_args(&export.args, input); @@ -514,7 +499,7 @@ pub fn invoke_export(resource: &ResourceManifest, cwd: &str, input: Option<&str> args = process_args(&export.args, ""); } - let (exit_code, stdout, stderr) = invoke_command(&export.executable, args, export_input.as_deref(), Some(cwd), env)?; + let (exit_code, stdout, stderr) = invoke_command(&export.executable, args, command_input.stdin.as_deref(), Some(cwd), command_input.env)?; log_resource_traces(&stderr); if exit_code != 0 { return Err(DscError::Command(resource.resource_type.clone(), exit_code, stderr)); @@ -638,22 +623,30 @@ fn process_args(args: &Option>, value: &str) -> Option> Some(processed_args) } -fn get_env_stdin(input_kind: &Option, input: &str) -> Result<(Option>, Option), DscError> { +struct CommandInput { + env: Option>, + stdin: Option, +} + +fn get_command_input(input_kind: &Option, input: &str) -> Result { let mut env: Option> = None; - let mut input_value: Option = None; + let mut stdin: Option = None; match input_kind { Some(InputKind::Env) => { env = Some(json_to_hashmap(input)?); }, Some(InputKind::Stdin) => { - input_value = Some(input.to_string()); + stdin = Some(input.to_string()); }, None => { // leave input as none }, } - Ok((env, input_value)) + Ok(CommandInput { + env, + stdin, + }) } fn verify_json(resource: &ResourceManifest, cwd: &str, json: &str) -> Result<(), DscError> { From bff600e6be1503aa10cd4b58d860f1b060f5d77b Mon Sep 17 00:00:00 2001 From: "Steve Lee (POWERSHELL HE/HIM) (from Dev Box)" Date: Fri, 12 Apr 2024 18:12:39 -0700 Subject: [PATCH 8/8] add tracing, fix winps resource manifest --- dsc_lib/src/dscresources/command_resource.rs | 8 ++++++-- powershell-adapter/Tests/powershellgroup.config.tests.ps1 | 4 ++-- .../Tests/powershellgroup.resource.tests.ps1 | 6 +++--- powershell-adapter/windowspowershell.dsc.resource.json | 3 ++- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/dsc_lib/src/dscresources/command_resource.rs b/dsc_lib/src/dscresources/command_resource.rs index 676a1252..c0718bc0 100644 --- a/dsc_lib/src/dscresources/command_resource.rs +++ b/dsc_lib/src/dscresources/command_resource.rs @@ -562,13 +562,14 @@ pub fn invoke_command(executable: &str, args: Option>, input: Option } let mut child = command.spawn()?; - if input.is_some() { + if let Some(input) = input { + trace!("Writing stdin to command: {input}"); // pipe to child stdin in a scope so that it is dropped before we wait // otherwise the pipe isn't closed and the child process waits forever let Some(mut child_stdin) = child.stdin.take() else { return Err(DscError::CommandOperation("Failed to open stdin".to_string(), executable.to_string())); }; - child_stdin.write_all(input.unwrap_or_default().as_bytes())?; + child_stdin.write_all(input.as_bytes())?; child_stdin.flush()?; } @@ -633,12 +634,15 @@ fn get_command_input(input_kind: &Option, input: &str) -> Result = None; match input_kind { Some(InputKind::Env) => { + debug!("Parsing input as environment variables"); env = Some(json_to_hashmap(input)?); }, Some(InputKind::Stdin) => { + debug!("Parsing input as stdin"); stdin = Some(input.to_string()); }, None => { + debug!("No input kind specified"); // leave input as none }, } diff --git a/powershell-adapter/Tests/powershellgroup.config.tests.ps1 b/powershell-adapter/Tests/powershellgroup.config.tests.ps1 index 5c18d678..6ff9ac76 100644 --- a/powershell-adapter/Tests/powershellgroup.config.tests.ps1 +++ b/powershell-adapter/Tests/powershellgroup.config.tests.ps1 @@ -5,7 +5,7 @@ Describe 'PowerShell adapter resource tests' { BeforeAll { if ($isWindows) { - winrm quickconfig -quiet + winrm quickconfig -quiet -force } $OldPSModulePath = $env:PSModulePath $env:PSModulePath += [System.IO.Path]::PathSeparator + $PSScriptRoot @@ -28,7 +28,7 @@ Describe 'PowerShell adapter resource tests' { It 'Get works on config with File resource for WinPS' -Skip:(!$IsWindows){ - $testFile = 'c:\test.txt' + $testFile = "$testdrive\test.txt" 'test' | Set-Content -Path $testFile -Force $r = (Get-Content -Raw $winpsConfigPath).Replace('c:\test.txt',"$testFile") | dsc config get $LASTEXITCODE | Should -Be 0 diff --git a/powershell-adapter/Tests/powershellgroup.resource.tests.ps1 b/powershell-adapter/Tests/powershellgroup.resource.tests.ps1 index ae311395..c927ffa0 100644 --- a/powershell-adapter/Tests/powershellgroup.resource.tests.ps1 +++ b/powershell-adapter/Tests/powershellgroup.resource.tests.ps1 @@ -5,7 +5,7 @@ Describe 'PowerShell adapter resource tests' { BeforeAll { if ($isWindows) { - winrm quickconfig -quiet + winrm quickconfig -quiet -force } $OldPSModulePath = $env:PSModulePath $env:PSModulePath += [System.IO.Path]::PathSeparator + $PSScriptRoot @@ -33,7 +33,7 @@ Describe 'PowerShell adapter resource tests' { It 'Get works on Binary "File" resource' -Skip:(!$IsWindows){ - $testFile = 'c:\test.txt' + $testFile = "$testdrive\test.txt" 'test' | Set-Content -Path $testFile -Force $r = '{"DestinationPath":"' + $testFile.replace('\','\\') + '"}' | dsc resource get -r 'PSDesiredStateConfiguration/File' $LASTEXITCODE | Should -Be 0 @@ -43,7 +43,7 @@ Describe 'PowerShell adapter resource tests' { It 'Get works on traditional "Script" resource' -Skip:(!$IsWindows){ - $testFile = 'c:\test.txt' + $testFile = "$testdrive\test.txt" 'test' | Set-Content -Path $testFile -Force $r = '{"GetScript": "@{result = $(Get-Content ' + $testFile.replace('\','\\') + ')}", "SetScript": "throw", "TestScript": "throw"}' | dsc resource get -r 'PSDesiredStateConfiguration/Script' $LASTEXITCODE | Should -Be 0 diff --git a/powershell-adapter/windowspowershell.dsc.resource.json b/powershell-adapter/windowspowershell.dsc.resource.json index e9d52193..9f072980 100644 --- a/powershell-adapter/windowspowershell.dsc.resource.json +++ b/powershell-adapter/windowspowershell.dsc.resource.json @@ -28,7 +28,8 @@ "-NoProfile", "-Command", "$Input | ./powershell.resource.ps1 Get" - ] + ], + "input": "stdin" }, "set": { "executable": "powershell",