Skip to content

Commit 4f23c0f

Browse files
committed
connect output to result
1 parent 8b54ea3 commit 4f23c0f

File tree

5 files changed

+82
-4
lines changed

5 files changed

+82
-4
lines changed

lib/dsc-lib/locales/en-us.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,11 @@ nameResultNotString = "Resource name result is not a string"
7979
circularDependency = "Circular dependency or unresolvable parameter references detected in parameters: %{parameters}"
8080
userFunctionAlreadyDefined = "User function '%{name}' in namespace '%{namespace}' is already defined"
8181
addingUserFunction = "Adding user function '%{name}'"
82+
skippingOutput = "Skipping output for '%{name}' due to condition evaluating to false"
83+
outputValueNotDefined = "Output value '%{name}' is not defined"
84+
secureOutputSkipped = "Secure output '%{name}' is skipped"
85+
outputTypeNotMatched = "Output '%{name}' type does not match expected type '%{expected_type}'"
86+
copyNotSupported = "Copy for output '%{name}' is currently not supported"
8287

8388
[discovery.commandDiscovery]
8489
couldNotReadSetting = "Could not read 'resourcePath' setting"

lib/dsc-lib/src/configure/config_doc.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use schemars::{JsonSchema, json_schema};
77
use serde::{Deserialize, Serialize};
88
use serde_json::{Map, Value};
99
use std::{collections::HashMap, fmt::Display};
10+
use std::fmt::Display;
1011

1112
use crate::{dscerror::DscError, schemas::DscRepoSchema};
1213

lib/dsc-lib/src/configure/config_result.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
use schemars::JsonSchema;
55
use serde::{Deserialize, Serialize};
6+
use serde_json::{Map, Value};
67
use crate::dscresources::invoke_result::{GetResult, SetResult, TestResult};
78
use crate::configure::config_doc::{Configuration, Metadata};
89

@@ -54,6 +55,8 @@ pub struct ConfigurationGetResult {
5455
pub messages: Vec<ResourceMessage>,
5556
#[serde(rename = "hadErrors")]
5657
pub had_errors: bool,
58+
#[serde(skip_serializing_if = "Option::is_none")]
59+
pub outputs: Option<Map<String, Value>>,
5760
}
5861

5962
impl ConfigurationGetResult {
@@ -64,6 +67,7 @@ impl ConfigurationGetResult {
6467
results: Vec::new(),
6568
messages: Vec::new(),
6669
had_errors: false,
70+
outputs: None,
6771
}
6872
}
6973
}
@@ -85,6 +89,7 @@ impl From<ConfigurationTestResult> for ConfigurationGetResult {
8589
results,
8690
messages: test_result.messages,
8791
had_errors: test_result.had_errors,
92+
outputs: test_result.outputs,
8893
}
8994
}
9095
}
@@ -140,6 +145,8 @@ pub struct ConfigurationSetResult {
140145
pub messages: Vec<ResourceMessage>,
141146
#[serde(rename = "hadErrors")]
142147
pub had_errors: bool,
148+
#[serde(skip_serializing_if = "Option::is_none")]
149+
pub outputs: Option<Map<String, Value>>,
143150
}
144151

145152
impl ConfigurationSetResult {
@@ -150,6 +157,7 @@ impl ConfigurationSetResult {
150157
results: Vec::new(),
151158
messages: Vec::new(),
152159
had_errors: false,
160+
outputs: None,
153161
}
154162
}
155163
}
@@ -200,6 +208,8 @@ pub struct ConfigurationTestResult {
200208
pub messages: Vec<ResourceMessage>,
201209
#[serde(rename = "hadErrors")]
202210
pub had_errors: bool,
211+
#[serde(skip_serializing_if = "Option::is_none")]
212+
pub outputs: Option<Map<String, Value>>,
203213
}
204214

205215
impl ConfigurationTestResult {
@@ -210,6 +220,7 @@ impl ConfigurationTestResult {
210220
results: Vec::new(),
211221
messages: Vec::new(),
212222
had_errors: false,
223+
outputs: None,
213224
}
214225
}
215226
}
@@ -228,6 +239,8 @@ pub struct ConfigurationExportResult {
228239
pub messages: Vec<ResourceMessage>,
229240
#[serde(rename = "hadErrors")]
230241
pub had_errors: bool,
242+
#[serde(skip_serializing_if = "Option::is_none")]
243+
pub outputs: Option<Map<String, Value>>,
231244
}
232245

233246
impl ConfigurationExportResult {
@@ -238,6 +251,7 @@ impl ConfigurationExportResult {
238251
result: None,
239252
messages: Vec::new(),
240253
had_errors: false,
254+
outputs: None,
241255
}
242256
}
243257
}

lib/dsc-lib/src/configure/context.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ pub struct Context {
3636
pub system_root: PathBuf,
3737
pub user_functions: HashMap<String, UserFunctionDefinition>,
3838
pub variables: Map<String, Value>,
39+
pub outputs: Map<String, Value>,
3940
}
4041

4142
impl Context {
@@ -61,6 +62,8 @@ impl Context {
6162
system_root: get_default_os_system_root(),
6263
user_functions: HashMap::new(),
6364
variables: Map::new(),
65+
restart_required: None,
66+
outputs: Map::new(),
6467
}
6568
}
6669
}

lib/dsc-lib/src/configure/mod.rs

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4-
use crate::configure::config_doc::{ExecutionKind, Metadata, Resource, Parameter};
54
use crate::configure::context::{Context, ProcessMode};
6-
use crate::configure::{config_doc::RestartRequired, parameters::Input};
5+
use crate::configure::{config_doc::{ExecutionKind, Metadata, Parameter, Resource, RestartRequired, ValueOrCopy}, parameters::Input};
76
use crate::discovery::discovery_trait::DiscoveryFilter;
87
use crate::dscerror::DscError;
98
use crate::dscresources::{
@@ -413,6 +412,10 @@ impl Configurator {
413412
result.metadata = Some(
414413
self.get_result_metadata(Operation::Get)
415414
);
415+
self.process_output()?;
416+
if !self.context.outputs.is_empty() {
417+
result.outputs = Some(self.context.outputs.clone());
418+
}
416419
Ok(result)
417420
}
418421

@@ -584,6 +587,10 @@ impl Configurator {
584587
result.metadata = Some(
585588
self.get_result_metadata(Operation::Set)
586589
);
590+
self.process_output()?;
591+
if !self.context.outputs.is_empty() {
592+
result.outputs = Some(self.context.outputs.clone());
593+
}
587594
Ok(result)
588595
}
589596

@@ -662,6 +669,10 @@ impl Configurator {
662669
result.metadata = Some(
663670
self.get_result_metadata(Operation::Test)
664671
);
672+
self.process_output()?;
673+
if !self.context.outputs.is_empty() {
674+
result.outputs = Some(self.context.outputs.clone());
675+
}
665676
Ok(result)
666677
}
667678

@@ -724,6 +735,10 @@ impl Configurator {
724735
}
725736

726737
result.result = Some(conf);
738+
self.process_output()?;
739+
if !self.context.outputs.is_empty() {
740+
result.outputs = Some(self.context.outputs.clone());
741+
}
727742
Ok(result)
728743
}
729744

@@ -738,6 +753,46 @@ impl Configurator {
738753
Ok(false)
739754
}
740755

756+
pub fn process_output(&mut self) -> Result<(), DscError> {
757+
if self.config.outputs.is_none() || self.context.execution_type == ExecutionKind::WhatIf {
758+
return Ok(());
759+
}
760+
if let Some(outputs) = &self.config.outputs {
761+
for (name, output) in outputs {
762+
if let Some(condition) = &output.condition {
763+
let condition_result = self.statement_parser.parse_and_execute(condition, &self.context)?;
764+
if condition_result != Value::Bool(true) {
765+
info!("{}", t!("configure.mod.skippingOutput", name = name));
766+
continue;
767+
}
768+
}
769+
770+
match &output.value_or_copy {
771+
ValueOrCopy::Value(value) => {
772+
let value_result = self.statement_parser.parse_and_execute(&value, &self.context)?;
773+
if output.r#type == DataType::SecureString || output.r#type == DataType::SecureObject {
774+
warn!("{}", t!("configure.mod.secureOutputSkipped", name = name));
775+
continue;
776+
}
777+
if value_result.is_string() && output.r#type != DataType::String ||
778+
value_result.is_i64() && output.r#type != DataType::Int ||
779+
value_result.is_boolean() && output.r#type != DataType::Bool ||
780+
value_result.is_array() && output.r#type != DataType::Array ||
781+
value_result.is_object() && output.r#type != DataType::Object {
782+
return Err(DscError::Validation(t!("configure.mod.outputTypeNotMatch", name = name, expected_type = output.r#type).to_string()));
783+
}
784+
self.context.outputs.insert(name.clone(), value_result);
785+
},
786+
_ => {
787+
warn!("{}", t!("configure.mod.copyNotSupported", name = name));
788+
continue;
789+
}
790+
}
791+
}
792+
}
793+
Ok(())
794+
}
795+
741796
/// Set the mounted path for the configuration.
742797
///
743798
/// # Arguments
@@ -779,7 +834,7 @@ impl Configurator {
779834
if let Some(parameters_input) = parameters_input {
780835
trace!("parameters_input: {parameters_input}");
781836
let input_parameters: HashMap<String, Value> = serde_json::from_value::<Input>(parameters_input.clone())?.parameters;
782-
837+
783838
for (name, value) in input_parameters {
784839
if let Some(constraint) = parameters.get(&name) {
785840
debug!("Validating parameter '{name}'");
@@ -818,7 +873,7 @@ impl Configurator {
818873

819874
while !unresolved_parameters.is_empty() {
820875
let mut resolved_in_this_pass = Vec::new();
821-
876+
822877
for (name, parameter) in &unresolved_parameters {
823878
debug!("{}", t!("configure.mod.processingParameter", name = name));
824879
if let Some(default_value) = &parameter.default_value {

0 commit comments

Comments
 (0)