11// Copyright (c) Microsoft Corporation.
22// Licensed under the MIT License.
33
4- use crate :: configure:: config_doc:: { ExecutionKind , Metadata , Resource } ;
4+ use crate :: configure:: config_doc:: { ExecutionKind , Metadata , Resource , Parameter } ;
55use crate :: configure:: context:: { Context , ProcessMode } ;
66use crate :: configure:: { config_doc:: RestartRequired , parameters:: Input } ;
77use crate :: discovery:: discovery_trait:: DiscoveryFilter ;
@@ -767,7 +767,6 @@ impl Configurator {
767767 }
768768
769769 fn set_parameters ( & mut self , parameters_input : Option < & Value > , config : & Configuration ) -> Result < ( ) , DscError > {
770- // set default parameters first
771770 let Some ( parameters) = & config. parameters else {
772771 if parameters_input. is_none ( ) {
773772 info ! ( "{}" , t!( "configure.mod.noParameters" ) ) ;
@@ -776,66 +775,87 @@ impl Configurator {
776775 return Err ( DscError :: Validation ( t ! ( "configure.mod.noParametersDefined" ) . to_string ( ) ) ) ;
777776 } ;
778777
779- for ( name, parameter) in parameters {
780- debug ! ( "{}" , t!( "configure.mod.processingParameter" , name = name) ) ;
781- if let Some ( default_value) = & parameter. default_value {
782- debug ! ( "{}" , t!( "configure.mod.setDefaultParameter" , name = name) ) ;
783- // default values can be expressions
784- let value = if default_value. is_string ( ) {
785- if let Some ( value) = default_value. as_str ( ) {
786- self . context . process_mode = ProcessMode :: ParametersDefault ;
787- let result = self . statement_parser . parse_and_execute ( value, & self . context ) ?;
788- self . context . process_mode = ProcessMode :: Normal ;
789- result
778+ // process input parameters first
779+ if let Some ( parameters_input) = parameters_input {
780+ trace ! ( "parameters_input: {parameters_input}" ) ;
781+ let input_parameters: HashMap < String , Value > = serde_json:: from_value :: < Input > ( parameters_input. clone ( ) ) ?. parameters ;
782+
783+ for ( name, value) in input_parameters {
784+ if let Some ( constraint) = parameters. get ( & name) {
785+ debug ! ( "Validating parameter '{name}'" ) ;
786+ check_length ( & name, & value, constraint) ?;
787+ check_allowed_values ( & name, & value, constraint) ?;
788+ check_number_limits ( & name, & value, constraint) ?;
789+ // TODO: additional array constraints
790+ // TODO: object constraints
791+
792+ validate_parameter_type ( & name, & value, & constraint. parameter_type ) ?;
793+ if constraint. parameter_type == DataType :: SecureString || constraint. parameter_type == DataType :: SecureObject {
794+ info ! ( "{}" , t!( "configure.mod.setSecureParameter" , name = name) ) ;
790795 } else {
791- return Err ( DscError :: Parser ( t ! ( "configure.mod.defaultStringNotDefined" ) . to_string ( ) ) ) ;
796+ info ! ( "{}" , t!( "configure.mod.setParameter" , name = name , value = value ) ) ;
792797 }
793- } else {
794- default_value. clone ( )
795- } ;
796- validate_parameter_type ( name, & value, & parameter. parameter_type ) ?;
797- self . context . parameters . insert ( name. clone ( ) , ( value, parameter. parameter_type . clone ( ) ) ) ;
798+
799+ self . context . parameters . insert ( name. clone ( ) , ( value. clone ( ) , constraint. parameter_type . clone ( ) ) ) ;
800+ if let Some ( parameters) = & mut self . config . parameters {
801+ if let Some ( parameter) = parameters. get_mut ( & name) {
802+ parameter. default_value = Some ( value) ;
803+ }
804+ }
805+ }
806+ else {
807+ return Err ( DscError :: Validation ( t ! ( "configure.mod.parameterNotDefined" , name = name) . to_string ( ) ) ) ;
808+ }
798809 }
799810 }
800811
801- let Some ( parameters_input) = parameters_input else {
802- debug ! ( "{}" , t!( "configure.mod.noParametersInput" ) ) ;
803- return Ok ( ( ) ) ;
804- } ;
812+ // Now process default values for parameters that weren't provided in input
813+ let mut unresolved_parameters: HashMap < String , & Parameter > = parameters
814+ . iter ( )
815+ . filter ( |( name, _) | !self . context . parameters . contains_key ( * name) )
816+ . map ( |( k, v) | ( k. clone ( ) , v) )
817+ . collect ( ) ;
818+
819+ while !unresolved_parameters. is_empty ( ) {
820+ let mut resolved_in_this_pass = Vec :: new ( ) ;
821+
822+ for ( name, parameter) in & unresolved_parameters {
823+ debug ! ( "{}" , t!( "configure.mod.processingParameter" , name = name) ) ;
824+ if let Some ( default_value) = & parameter. default_value {
825+ debug ! ( "{}" , t!( "configure.mod.setDefaultParameter" , name = name) ) ;
826+ let value_result = if default_value. is_string ( ) {
827+ if let Some ( value) = default_value. as_str ( ) {
828+ self . context . process_mode = ProcessMode :: ParametersDefault ;
829+ let result = self . statement_parser . parse_and_execute ( value, & self . context ) ;
830+ self . context . process_mode = ProcessMode :: Normal ;
831+ result
832+ } else {
833+ return Err ( DscError :: Parser ( t ! ( "configure.mod.defaultStringNotDefined" ) . to_string ( ) ) ) ;
834+ }
835+ } else {
836+ Ok ( default_value. clone ( ) )
837+ } ;
805838
806- trace ! ( "parameters_input: {parameters_input}" ) ;
807- let parameters: HashMap < String , Value > = serde_json:: from_value :: < Input > ( parameters_input. clone ( ) ) ?. parameters ;
808- let Some ( parameters_constraints) = & config. parameters else {
809- return Err ( DscError :: Validation ( t ! ( "configure.mod.noParametersDefined" ) . to_string ( ) ) ) ;
810- } ;
811- for ( name, value) in parameters {
812- if let Some ( constraint) = parameters_constraints. get ( & name) {
813- debug ! ( "Validating parameter '{name}'" ) ;
814- check_length ( & name, & value, constraint) ?;
815- check_allowed_values ( & name, & value, constraint) ?;
816- check_number_limits ( & name, & value, constraint) ?;
817- // TODO: additional array constraints
818- // TODO: object constraints
819-
820- validate_parameter_type ( & name, & value, & constraint. parameter_type ) ?;
821- if constraint. parameter_type == DataType :: SecureString || constraint. parameter_type == DataType :: SecureObject {
822- info ! ( "{}" , t!( "configure.mod.setSecureParameter" , name = name) ) ;
839+ if let Ok ( value) = value_result {
840+ validate_parameter_type ( name, & value, & parameter. parameter_type ) ?;
841+ self . context . parameters . insert ( name. to_string ( ) , ( value, parameter. parameter_type . clone ( ) ) ) ;
842+ resolved_in_this_pass. push ( name. clone ( ) ) ;
843+ }
823844 } else {
824- info ! ( "{}" , t! ( "configure.mod.setParameter" , name = name , value = value ) ) ;
845+ resolved_in_this_pass . push ( name. clone ( ) ) ;
825846 }
847+ }
826848
827- self . context . parameters . insert ( name. clone ( ) , ( value. clone ( ) , constraint. parameter_type . clone ( ) ) ) ;
828- // also update the configuration with the parameter value
829- if let Some ( parameters) = & mut self . config . parameters {
830- if let Some ( parameter) = parameters. get_mut ( & name) {
831- parameter. default_value = Some ( value) ;
832- }
833- }
849+ if resolved_in_this_pass. is_empty ( ) {
850+ let unresolved_names: Vec < _ > = unresolved_parameters. keys ( ) . map ( std:: string:: String :: as_str) . collect ( ) ;
851+ return Err ( DscError :: Validation ( t ! ( "configure.mod.circularDependency" , parameters = unresolved_names. join( ", " ) ) . to_string ( ) ) ) ;
834852 }
835- else {
836- return Err ( DscError :: Validation ( t ! ( "configure.mod.parameterNotDefined" , name = name) . to_string ( ) ) ) ;
853+
854+ for name in & resolved_in_this_pass {
855+ unresolved_parameters. remove ( name) ;
837856 }
838857 }
858+
839859 Ok ( ( ) )
840860 }
841861
0 commit comments