33
44use jsonschema:: JSONSchema ;
55use serde_json:: Value ;
6- use std:: { process:: Command , io:: { Write , Read } , process:: Stdio } ;
7-
6+ use std:: { collections:: HashMap , process:: Command , io:: { Write , Read } , process:: Stdio } ;
87use crate :: dscerror:: DscError ;
9- use super :: { dscresource:: get_diff, resource_manifest:: { ResourceManifest , ReturnKind , SchemaKind } , invoke_result:: { GetResult , SetResult , TestResult , ValidateResult , ExportResult } } ;
8+ use super :: { dscresource:: get_diff, resource_manifest:: { ResourceManifest , InputKind , ReturnKind , SchemaKind } , invoke_result:: { GetResult , SetResult , TestResult , ValidateResult , ExportResult } } ;
109
1110pub const EXIT_PROCESS_TERMINATED : i32 = 0x102 ;
1211
@@ -21,12 +20,27 @@ pub const EXIT_PROCESS_TERMINATED: i32 = 0x102;
2120///
2221/// Error returned if the resource does not successfully get the current state
2322pub fn invoke_get ( resource : & ResourceManifest , cwd : & str , filter : & str ) -> Result < GetResult , DscError > {
24- if !filter. is_empty ( ) && resource. get . input . is_some ( ) {
23+ let input_kind = if let Some ( input_kind) = & resource. get . input {
24+ input_kind. clone ( )
25+ }
26+ else {
27+ InputKind :: Stdin
28+ } ;
29+
30+ let mut env: Option < HashMap < String , String > > = None ;
31+ let mut input_filter: Option < & str > = None ;
32+ if !filter. is_empty ( ) {
2533 verify_json ( resource, cwd, filter) ?;
34+
35+ if input_kind == InputKind :: Env {
36+ env = Some ( json_to_hashmap ( filter) ?) ;
37+ }
38+ else {
39+ input_filter = Some ( filter) ;
40+ }
2641 }
2742
28- let ( exit_code, stdout, stderr) = invoke_command ( & resource. get . executable , resource. get . args . clone ( ) , Some ( filter) , Some ( cwd) ) ?;
29- //println!("{stdout}");
43+ let ( exit_code, stdout, stderr) = invoke_command ( & resource. get . executable , resource. get . args . clone ( ) , input_filter, Some ( cwd) , env) ?;
3044 if exit_code != 0 {
3145 return Err ( DscError :: Command ( resource. resource_type . clone ( ) , exit_code, stderr) ) ;
3246 }
@@ -59,6 +73,16 @@ pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_te
5973 return Err ( DscError :: NotImplemented ( "set" . to_string ( ) ) ) ;
6074 } ;
6175 verify_json ( resource, cwd, desired) ?;
76+
77+ let mut env: Option < HashMap < String , String > > = None ;
78+ let mut input_desired: Option < & str > = None ;
79+ if set. input == InputKind :: Env {
80+ env = Some ( json_to_hashmap ( desired) ?) ;
81+ }
82+ else {
83+ input_desired = Some ( desired) ;
84+ }
85+
6286 // if resource doesn't implement a pre-test, we execute test first to see if a set is needed
6387 if !skip_test && !set. pre_test . unwrap_or_default ( ) {
6488 let test_result = invoke_test ( resource, cwd, desired) ?;
@@ -70,14 +94,34 @@ pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_te
7094 } ) ;
7195 }
7296 }
73- let ( exit_code, stdout, stderr) = invoke_command ( & resource. get . executable , resource. get . args . clone ( ) , Some ( desired) , Some ( cwd) ) ?;
97+
98+ let mut get_env: Option < HashMap < String , String > > = None ;
99+ let mut get_input: Option < & str > = None ;
100+ match & resource. get . input {
101+ Some ( InputKind :: Env ) => {
102+ get_env = Some ( json_to_hashmap ( desired) ?) ;
103+ } ,
104+ Some ( InputKind :: Stdin ) => {
105+ get_input = Some ( desired) ;
106+ } ,
107+ None => {
108+ // leave input as none
109+ } ,
110+ }
111+
112+ let ( exit_code, stdout, stderr) = invoke_command ( & resource. get . executable , resource. get . args . clone ( ) , get_input, Some ( cwd) , get_env) ?;
113+ if exit_code != 0 {
114+ return Err ( DscError :: Command ( resource. resource_type . clone ( ) , exit_code, stderr) ) ;
115+ }
116+
74117 let pre_state: Value = if exit_code == 0 {
75118 serde_json:: from_str ( & stdout) ?
76119 }
77120 else {
78121 return Err ( DscError :: Command ( resource. resource_type . clone ( ) , exit_code, stderr) ) ;
79122 } ;
80- let ( exit_code, stdout, stderr) = invoke_command ( & set. executable , set. args . clone ( ) , Some ( desired) , Some ( cwd) ) ?;
123+
124+ let ( exit_code, stdout, stderr) = invoke_command ( & set. executable , set. args . clone ( ) , input_desired, Some ( cwd) , env) ?;
81125 if exit_code != 0 {
82126 return Err ( DscError :: Command ( resource. resource_type . clone ( ) , exit_code, stderr) ) ;
83127 }
@@ -147,7 +191,17 @@ pub fn invoke_test(resource: &ResourceManifest, cwd: &str, expected: &str) -> Re
147191 } ;
148192
149193 verify_json ( resource, cwd, expected) ?;
150- let ( exit_code, stdout, stderr) = invoke_command ( & test. executable , test. args . clone ( ) , Some ( expected) , Some ( cwd) ) ?;
194+
195+ let mut env: Option < HashMap < String , String > > = None ;
196+ let mut input_expected: Option < & str > = None ;
197+ if test. input == InputKind :: Env {
198+ env = Some ( json_to_hashmap ( expected) ?) ;
199+ }
200+ else {
201+ input_expected = Some ( expected) ;
202+ }
203+
204+ let ( exit_code, stdout, stderr) = invoke_command ( & test. executable , test. args . clone ( ) , input_expected, Some ( cwd) , env) ?;
151205 if exit_code != 0 {
152206 return Err ( DscError :: Command ( resource. resource_type . clone ( ) , exit_code, stderr) ) ;
153207 }
@@ -222,7 +276,7 @@ pub fn invoke_validate(resource: &ResourceManifest, cwd: &str, config: &str) ->
222276 return Err ( DscError :: NotImplemented ( "validate" . to_string ( ) ) ) ;
223277 } ;
224278
225- let ( exit_code, stdout, stderr) = invoke_command ( & validate. executable , validate. args . clone ( ) , Some ( config) , Some ( cwd) ) ?;
279+ let ( exit_code, stdout, stderr) = invoke_command ( & validate. executable , validate. args . clone ( ) , Some ( config) , Some ( cwd) , None ) ?;
226280 if exit_code != 0 {
227281 return Err ( DscError :: Command ( resource. resource_type . clone ( ) , exit_code, stderr) ) ;
228282 }
@@ -247,7 +301,7 @@ pub fn get_schema(resource: &ResourceManifest, cwd: &str) -> Result<String, DscE
247301
248302 match schema_kind {
249303 SchemaKind :: Command ( ref command) => {
250- let ( exit_code, stdout, stderr) = invoke_command ( & command. executable , command. args . clone ( ) , None , Some ( cwd) ) ?;
304+ let ( exit_code, stdout, stderr) = invoke_command ( & command. executable , command. args . clone ( ) , None , Some ( cwd) , None ) ?;
251305 if exit_code != 0 {
252306 return Err ( DscError :: Command ( resource. resource_type . clone ( ) , exit_code, stderr) ) ;
253307 }
@@ -291,7 +345,7 @@ pub fn invoke_export(resource: &ResourceManifest, cwd: &str) -> Result<ExportRes
291345 return Err ( DscError :: Operation ( format ! ( "Export is not supported by resource {}" , & resource. resource_type) ) )
292346 } ;
293347
294- let ( exit_code, stdout, stderr) = invoke_command ( & export. executable , export. args . clone ( ) , None , Some ( cwd) ) ?;
348+ let ( exit_code, stdout, stderr) = invoke_command ( & export. executable , export. args . clone ( ) , None , Some ( cwd) , None ) ?;
295349 if exit_code != 0 {
296350 return Err ( DscError :: Command ( resource. resource_type . clone ( ) , exit_code, stderr) ) ;
297351 }
@@ -324,7 +378,8 @@ pub fn invoke_export(resource: &ResourceManifest, cwd: &str) -> Result<ExportRes
324378/// # Errors
325379///
326380/// Error is returned if the command fails to execute or stdin/stdout/stderr cannot be opened.
327- pub fn invoke_command ( executable : & str , args : Option < Vec < String > > , input : Option < & str > , cwd : Option < & str > ) -> Result < ( i32 , String , String ) , DscError > {
381+ #[ allow( clippy:: implicit_hasher) ]
382+ pub fn invoke_command ( executable : & str , args : Option < Vec < String > > , input : Option < & str > , cwd : Option < & str > , env : Option < HashMap < String , String > > ) -> Result < ( i32 , String , String ) , DscError > {
328383 let mut command = Command :: new ( executable) ;
329384 if input. is_some ( ) {
330385 command. stdin ( Stdio :: piped ( ) ) ;
@@ -337,6 +392,9 @@ pub fn invoke_command(executable: &str, args: Option<Vec<String>>, input: Option
337392 if let Some ( cwd) = cwd {
338393 command. current_dir ( cwd) ;
339394 }
395+ if let Some ( env) = env {
396+ command. envs ( env) ;
397+ }
340398
341399 let mut child = command. spawn ( ) ?;
342400 if input. is_some ( ) {
@@ -399,3 +457,48 @@ fn verify_json(resource: &ResourceManifest, cwd: &str, json: &str) -> Result<(),
399457 } ;
400458 result
401459}
460+
461+ fn json_to_hashmap ( json : & str ) -> Result < HashMap < String , String > , DscError > {
462+ let mut map = HashMap :: new ( ) ;
463+ let json: Value = serde_json:: from_str ( json) ?;
464+ if let Value :: Object ( obj) = json {
465+ for ( key, value) in obj {
466+ match value {
467+ Value :: String ( s) => {
468+ map. insert ( key, s) ;
469+ } ,
470+ Value :: Bool ( b) => {
471+ map. insert ( key, b. to_string ( ) ) ;
472+ } ,
473+ Value :: Number ( n) => {
474+ map. insert ( key, n. to_string ( ) ) ;
475+ } ,
476+ Value :: Array ( a) => {
477+ // only array of number or strings is supported
478+ let mut array = Vec :: new ( ) ;
479+ for v in a {
480+ match v {
481+ Value :: String ( s) => {
482+ array. push ( s) ;
483+ } ,
484+ Value :: Number ( n) => {
485+ array. push ( n. to_string ( ) ) ;
486+ } ,
487+ _ => {
488+ return Err ( DscError :: Operation ( format ! ( "Unsupported array value for key {key}. Only string and number is supported." ) ) ) ;
489+ } ,
490+ }
491+ }
492+ map. insert ( key, array. join ( "," ) ) ;
493+ } ,
494+ Value :: Null => {
495+ continue ;
496+ }
497+ Value :: Object ( _) => {
498+ return Err ( DscError :: Operation ( format ! ( "Unsupported value for key {key}. Only string, bool, number, and array is supported." ) ) ) ;
499+ } ,
500+ }
501+ }
502+ }
503+ Ok ( map)
504+ }
0 commit comments