33
44use jsonschema:: JSONSchema ;
55use serde_json:: Value ;
6- use std:: { collections:: HashMap , env, process :: Command , io:: { Write , Read } , process:: Stdio } ;
6+ use std:: { collections:: HashMap , env, io:: { Read , Write } , process:: { Command , Stdio } } ;
77use crate :: { dscerror:: DscError , dscresources:: invoke_result:: { ResourceGetResponse , ResourceSetResponse , ResourceTestResponse } } ;
88use crate :: configure:: config_result:: ResourceGetResult ;
9- use super :: { dscresource:: get_diff, resource_manifest :: { Kind , ResourceManifest , InputKind , ReturnKind , SchemaKind } , invoke_result :: { GetResult , SetResult , TestResult , ValidateResult , ExportResult } } ;
9+ use super :: { dscresource:: get_diff, invoke_result :: { ExportResult , GetResult , SetResult , TestResult , ValidateResult } , resource_manifest :: { ArgKind , InputKind , Kind , ResourceManifest , ReturnKind , SchemaKind } } ;
1010use tracing:: { error, warn, info, debug, trace} ;
1111
1212pub const EXIT_PROCESS_TERMINATED : i32 = 0x102 ;
@@ -54,7 +54,7 @@ pub fn invoke_get(resource: &ResourceManifest, cwd: &str, filter: &str) -> Resul
5454
5555 let mut env: Option < HashMap < String , String > > = None ;
5656 let mut input_filter: Option < & str > = None ;
57- let mut get_args = resource . get . args . clone ( ) ;
57+ let mut args : Option < Vec < String > > = None ;
5858 if !filter. is_empty ( ) {
5959 verify_json ( resource, cwd, filter) ?;
6060
@@ -65,14 +65,14 @@ pub fn invoke_get(resource: &ResourceManifest, cwd: &str, filter: &str) -> Resul
6565 InputKind :: Stdin => {
6666 input_filter = Some ( filter) ;
6767 } ,
68- InputKind :: Arg ( arg_name ) => {
69- replace_token ( & mut get_args , & arg_name , filter) ? ;
68+ InputKind :: Arg => {
69+ args = process_args ( & resource . get . args , filter) ;
7070 } ,
7171 }
7272 }
7373
7474 info ! ( "Invoking get '{}' using '{}'" , & resource. resource_type, & resource. get. executable) ;
75- let ( exit_code, stdout, stderr) = invoke_command ( & resource. get . executable , get_args , input_filter, Some ( cwd) , env) ?;
75+ let ( exit_code, stdout, stderr) = invoke_command ( & resource. get . executable , args , input_filter, Some ( cwd) , env) ?;
7676 log_resource_traces ( & stderr) ;
7777 if exit_code != 0 {
7878 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
114114/// Error returned if the resource does not successfully set the desired state
115115#[ allow( clippy:: too_many_lines) ]
116116pub fn invoke_set ( resource : & ResourceManifest , cwd : & str , desired : & str , skip_test : bool ) -> Result < SetResult , DscError > {
117- let Some ( set) = resource. set . as_ref ( ) else {
117+ let Some ( set) = & resource. set else {
118118 return Err ( DscError :: NotImplemented ( "set" . to_string ( ) ) ) ;
119119 } ;
120120 verify_json ( resource, cwd, desired) ?;
@@ -146,24 +146,24 @@ pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_te
146146
147147 let mut get_env: Option < HashMap < String , String > > = None ;
148148 let mut get_input: Option < & str > = None ;
149- let mut get_args = resource . get . args . clone ( ) ;
149+ let mut args : Option < Vec < String > > = None ;
150150 match & resource. get . input {
151151 Some ( InputKind :: Env ) => {
152152 get_env = Some ( json_to_hashmap ( desired) ?) ;
153153 } ,
154154 Some ( InputKind :: Stdin ) => {
155155 get_input = Some ( desired) ;
156156 } ,
157- Some ( InputKind :: Arg ( arg_token ) ) => {
158- replace_token ( & mut get_args , arg_token , desired) ? ;
157+ Some ( InputKind :: Arg ) => {
158+ args = process_args ( & resource . get . args , desired) ;
159159 } ,
160160 None => {
161161 // leave input as none
162162 } ,
163163 }
164164
165165 info ! ( "Getting current state for set by invoking get {} using {}" , & resource. resource_type, & resource. get. executable) ;
166- let ( exit_code, stdout, stderr) = invoke_command ( & resource. get . executable , get_args , get_input, Some ( cwd) , get_env) ?;
166+ let ( exit_code, stdout, stderr) = invoke_command ( & resource. get . executable , args , get_input, Some ( cwd) , get_env) ?;
167167 log_resource_traces ( & stderr) ;
168168 if exit_code != 0 {
169169 return Err ( DscError :: Command ( resource. resource_type . clone ( ) , exit_code, stderr) ) ;
@@ -183,16 +183,16 @@ pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_te
183183
184184 let mut env: Option < HashMap < String , String > > = None ;
185185 let mut input_desired: Option < & str > = None ;
186- let mut args = set . args . clone ( ) ;
186+ let mut args: Option < Vec < String > > = None ;
187187 match & set. input {
188188 InputKind :: Env => {
189189 env = Some ( json_to_hashmap ( desired) ?) ;
190190 } ,
191191 InputKind :: Stdin => {
192192 input_desired = Some ( desired) ;
193193 } ,
194- InputKind :: Arg ( arg_token ) => {
195- replace_token ( & mut args, arg_token , desired) ? ;
194+ InputKind :: Arg => {
195+ args = process_args ( & set . args , desired) ;
196196 } ,
197197 }
198198
@@ -289,16 +289,16 @@ pub fn invoke_test(resource: &ResourceManifest, cwd: &str, expected: &str) -> Re
289289
290290 let mut env: Option < HashMap < String , String > > = None ;
291291 let mut input_expected: Option < & str > = None ;
292- let mut args = test . args . clone ( ) ;
292+ let mut args: Option < Vec < String > > = None ;
293293 match & test. input {
294294 InputKind :: Env => {
295295 env = Some ( json_to_hashmap ( expected) ?) ;
296296 } ,
297297 InputKind :: Stdin => {
298298 input_expected = Some ( expected) ;
299299 } ,
300- InputKind :: Arg ( arg_token ) => {
301- replace_token ( & mut args, arg_token , expected) ? ;
300+ InputKind :: Arg => {
301+ args = process_args ( & test . args , expected) ;
302302 } ,
303303 }
304304
@@ -376,15 +376,15 @@ pub fn invoke_test(resource: &ResourceManifest, cwd: &str, expected: &str) -> Re
376376}
377377
378378/// Invoke the delete operation against a command resource.
379- ///
379+ ///
380380/// # Arguments
381- ///
381+ ///
382382/// * `resource` - The resource manifest for the command resource.
383383/// * `cwd` - The current working directory.
384384/// * `filter` - The filter to apply to the resource in JSON.
385- ///
385+ ///
386386/// # Errors
387- ///
387+ ///
388388/// Error is returned if the underlying command returns a non-zero exit code.
389389pub fn invoke_delete ( resource : & ResourceManifest , cwd : & str , filter : & str ) -> Result < ( ) , DscError > {
390390 let Some ( delete) = & resource. delete else {
@@ -393,7 +393,7 @@ pub fn invoke_delete(resource: &ResourceManifest, cwd: &str, filter: &str) -> Re
393393
394394 let mut env: Option < HashMap < String , String > > = None ;
395395 let mut input_filter: Option < & str > = None ;
396- let mut get_args = resource . get . args . clone ( ) ;
396+ let mut args : Option < Vec < String > > = None ;
397397 verify_json ( resource, cwd, filter) ?;
398398 match & delete. input {
399399 InputKind :: Env => {
@@ -402,13 +402,13 @@ pub fn invoke_delete(resource: &ResourceManifest, cwd: &str, filter: &str) -> Re
402402 InputKind :: Stdin => {
403403 input_filter = Some ( filter) ;
404404 } ,
405- InputKind :: Arg ( arg_name ) => {
406- replace_token ( & mut get_args , arg_name , filter) ? ;
405+ InputKind :: Arg => {
406+ args = process_args ( & delete . args , filter) ;
407407 } ,
408408 }
409409
410410 info ! ( "Invoking delete '{}' using '{}'" , & resource. resource_type, & delete. executable) ;
411- let ( exit_code, _stdout, stderr) = invoke_command ( & delete. executable , get_args , input_filter, Some ( cwd) , env) ?;
411+ let ( exit_code, _stdout, stderr) = invoke_command ( & delete. executable , args , input_filter, Some ( cwd) , env) ?;
412412 log_resource_traces ( & stderr) ;
413413 if exit_code != 0 {
414414 return Err ( DscError :: Command ( resource. resource_type . clone ( ) , exit_code, stderr) ) ;
@@ -439,7 +439,22 @@ pub fn invoke_validate(resource: &ResourceManifest, cwd: &str, config: &str) ->
439439 return Err ( DscError :: NotImplemented ( "validate" . to_string ( ) ) ) ;
440440 } ;
441441
442- let ( exit_code, stdout, stderr) = invoke_command ( & validate. executable , validate. args . clone ( ) , Some ( config) , Some ( cwd) , None ) ?;
442+ let mut env: Option < HashMap < String , String > > = None ;
443+ let mut input_config: Option < & str > = None ;
444+ let mut args: Option < Vec < String > > = None ;
445+ match & validate. input {
446+ InputKind :: Env => {
447+ env = Some ( json_to_hashmap ( config) ?) ;
448+ } ,
449+ InputKind :: Stdin => {
450+ input_config = Some ( config) ;
451+ } ,
452+ InputKind :: Arg => {
453+ args = process_args ( & validate. args , config) ;
454+ } ,
455+ }
456+
457+ let ( exit_code, stdout, stderr) = invoke_command ( & validate. executable , args, input_config, Some ( cwd) , env) ?;
443458 log_resource_traces ( & stderr) ;
444459 if exit_code != 0 {
445460 return Err ( DscError :: Command ( resource. resource_type . clone ( ) , exit_code, stderr) ) ;
@@ -511,7 +526,27 @@ pub fn invoke_export(resource: &ResourceManifest, cwd: &str, input: Option<&str>
511526 return Err ( DscError :: Operation ( format ! ( "Export is not supported by resource {}" , & resource. resource_type) ) )
512527 } ;
513528
514- let ( exit_code, stdout, stderr) = invoke_command ( & export. executable , export. args . clone ( ) , input, Some ( cwd) , None ) ?;
529+ let mut env: Option < HashMap < String , String > > = None ;
530+ let mut export_input: Option < & str > = None ;
531+ let mut args: Option < Vec < String > > = None ;
532+ if let Some ( input) = input {
533+ match & export. input {
534+ Some ( InputKind :: Env ) => {
535+ env = Some ( json_to_hashmap ( input) ?) ;
536+ } ,
537+ Some ( InputKind :: Stdin ) => {
538+ export_input = Some ( input) ;
539+ } ,
540+ Some ( InputKind :: Arg ) => {
541+ args = process_args ( & export. args , input) ;
542+ } ,
543+ None => {
544+ // leave input as none
545+ } ,
546+ }
547+ }
548+
549+ let ( exit_code, stdout, stderr) = invoke_command ( & export. executable , args, export_input, Some ( cwd) , env) ?;
515550 log_resource_traces ( & stderr) ;
516551 if exit_code != 0 {
517552 return Err ( DscError :: Command ( resource. resource_type . clone ( ) , exit_code, stderr) ) ;
@@ -609,24 +644,30 @@ pub fn invoke_command(executable: &str, args: Option<Vec<String>>, input: Option
609644 Ok ( ( exit_code, stdout, stderr) )
610645}
611646
612- fn replace_token ( args : & mut Option < Vec < String > > , token : & str , value : & str ) -> Result < ( ) , DscError > {
647+ fn process_args ( args : & Option < Vec < ArgKind > > , value : & str ) -> Option < Vec < String > > {
613648 let Some ( arg_values) = args else {
614- return Err ( DscError :: Operation ( "No args to replace" . to_string ( ) ) ) ;
649+ debug ! ( "No args to process" ) ;
650+ return None ;
615651 } ;
616652
617- let mut found = false ;
653+ let mut processed_args = Vec :: < String > :: new ( ) ;
618654 for arg in arg_values {
619- if arg == token {
620- found = true ;
621- * arg = value. to_string ( ) ;
622- }
623- }
655+ match arg {
656+ ArgKind :: String ( s) => {
657+ processed_args. push ( s. clone ( ) ) ;
658+ } ,
659+ ArgKind :: Json { json_input_arg, mandatory } => {
660+ if value. is_empty ( ) && * mandatory == Some ( true ) {
661+ continue ;
662+ }
624663
625- if !found {
626- return Err ( DscError :: Operation ( format ! ( "Token {token} not found in args" ) ) ) ;
664+ processed_args. push ( json_input_arg. clone ( ) ) ;
665+ processed_args. push ( value. to_string ( ) ) ;
666+ } ,
667+ }
627668 }
628669
629- Ok ( ( ) )
670+ Some ( processed_args )
630671}
631672
632673fn verify_json ( resource : & ResourceManifest , cwd : & str , json : & str ) -> Result < ( ) , DscError > {
0 commit comments