Skip to content

Commit 86ed4a8

Browse files
committed
turn include into a group resource
1 parent 134960c commit 86ed4a8

File tree

5 files changed

+112
-60
lines changed

5 files changed

+112
-60
lines changed

dsc/include.dsc.resource.json

+53-32
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,55 @@
11
{
2-
"$schema": "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/bundled/resource/manifest.json",
3-
"type": "Microsoft.DSC/Include",
4-
"version": "0.1.0",
5-
"description": "Allows including a configuration file contents into current configuration.",
6-
"kind": "Import",
7-
"resolve": {
8-
"executable": "dsc",
9-
"args": [
10-
"config",
11-
"resolve"
12-
],
13-
"input": "stdin"
14-
},
15-
"exitCodes": {
16-
"0": "Success",
17-
"1": "Invalid argument",
18-
"2": "Resource error",
19-
"3": "JSON Serialization error",
20-
"4": "Invalid input format",
21-
"5": "Resource instance failed schema validation",
22-
"6": "Command cancelled"
23-
},
24-
"schema": {
25-
"command": {
26-
"executable": "dsc",
27-
"args": [
28-
"schema",
29-
"--type",
30-
"include"
31-
]
32-
}
33-
}
2+
"$schema": "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/bundled/resource/manifest.json",
3+
"type": "Microsoft.DSC/Include",
4+
"version": "0.1.0",
5+
"description": "Allows including a configuration file with optional parameter file.",
6+
"kind": "Group",
7+
"get": {
8+
"executable": "dsc",
9+
"args": [
10+
"config",
11+
"--as-include",
12+
"get"
13+
],
14+
"input": "stdin"
15+
},
16+
"set": {
17+
"executable": "dsc",
18+
"args": [
19+
"config",
20+
"--as-include",
21+
"set"
22+
],
23+
"input": "stdin",
24+
"implementsPretest": true,
25+
"return": "state"
26+
},
27+
"test": {
28+
"executable": "dsc",
29+
"args": [
30+
"config",
31+
"--as-include",
32+
"test"
33+
],
34+
"input": "stdin",
35+
"return": "state"
36+
},
37+
"exitCodes": {
38+
"0": "Success",
39+
"1": "Invalid argument",
40+
"2": "Resource error",
41+
"3": "JSON Serialization error",
42+
"4": "Invalid input format",
43+
"5": "Resource instance failed schema validation",
44+
"6": "Command cancelled"
45+
},
46+
"validate": {
47+
"executable": "dsc",
48+
"args": [
49+
"config",
50+
"--as-include",
51+
"validate"
52+
],
53+
"input": "stdin"
3454
}
55+
}

dsc/src/args.rs

+3
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ pub enum SubCommand {
5757
// Used to inform when DSC is used as a group resource to modify it's output
5858
#[clap(long, hide = true)]
5959
as_group: bool,
60+
// Used to inform when DSC is used as a include group resource
61+
#[clap(long, hide = true)]
62+
as_include: bool,
6063
},
6164
#[clap(name = "resource", about = "Invoke a specific DSC resource")]
6265
Resource {

dsc/src/main.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -69,19 +69,19 @@ fn main() {
6969
let mut cmd = Args::command();
7070
generate(shell, &mut cmd, "dsc", &mut io::stdout());
7171
},
72-
SubCommand::Config { subcommand, parameters, parameters_file, as_group } => {
72+
SubCommand::Config { subcommand, parameters, parameters_file, as_group, as_include } => {
7373
if let Some(file_name) = parameters_file {
7474
info!("Reading parameters from file {file_name}");
7575
match std::fs::read_to_string(&file_name) {
76-
Ok(parameters) => subcommand::config(&subcommand, &Some(parameters), &input, &as_group),
76+
Ok(parameters) => subcommand::config(&subcommand, &Some(parameters), &input, &as_group, &as_include),
7777
Err(err) => {
7878
error!("Error: Failed to read parameters file '{file_name}': {err}");
7979
exit(util::EXIT_INVALID_INPUT);
8080
}
8181
}
8282
}
8383
else {
84-
subcommand::config(&subcommand, &parameters, &input, &as_group);
84+
subcommand::config(&subcommand, &parameters, &input, &as_group, &as_include);
8585
}
8686
},
8787
SubCommand::Resource { subcommand } => {

dsc/src/subcommand.rs

+18-6
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use crate::resource_command::{get_resource, self};
77
use crate::Stream;
88
use crate::tablewriter::Table;
99
use crate::util::{DSC_CONFIG_ROOT, EXIT_DSC_ERROR, EXIT_INVALID_INPUT, EXIT_JSON_ERROR, EXIT_VALIDATION_FAILED, get_schema, write_output, get_input, set_dscconfigroot, validate_json};
10-
use dsc_lib::configure::{Configurator, config_doc::ExecutionKind, config_result::ResourceGetResult};
10+
use dsc_lib::configure::{Configurator, config_doc::{Configuration, ExecutionKind}, config_result::ResourceGetResult};
1111
use dsc_lib::dscerror::DscError;
1212
use dsc_lib::dscresources::invoke_result::ResolveResult;
1313
use dsc_lib::{
@@ -186,15 +186,27 @@ fn initialize_config_root(path: &Option<String>) -> Option<String> {
186186
}
187187

188188
#[allow(clippy::too_many_lines)]
189-
pub fn config(subcommand: &ConfigSubCommand, parameters: &Option<String>, stdin: &Option<String>, as_group: &bool) {
189+
pub fn config(subcommand: &ConfigSubCommand, parameters: &Option<String>, stdin: &Option<String>, as_group: &bool, as_include: &bool) {
190190
let (new_parameters, json_string) = match subcommand {
191191
ConfigSubCommand::Get { document, path, .. } |
192192
ConfigSubCommand::Set { document, path, .. } |
193193
ConfigSubCommand::Test { document, path, .. } |
194194
ConfigSubCommand::Validate { document, path, .. } |
195195
ConfigSubCommand::Export { document, path, .. } => {
196196
let new_path = initialize_config_root(path);
197-
(None, get_input(document, stdin, &new_path))
197+
let input = get_input(document, stdin, &new_path);
198+
if *as_include {
199+
let (new_parameters, config_json) = match get_contents(&input) {
200+
Ok((parameters, config_json)) => (parameters, config_json),
201+
Err(err) => {
202+
error!("{err}");
203+
exit(EXIT_DSC_ERROR);
204+
}
205+
};
206+
(new_parameters, config_json)
207+
} else {
208+
(None, input)
209+
}
198210
},
199211
ConfigSubCommand::Resolve { document, path, .. } => {
200212
let new_path = initialize_config_root(path);
@@ -278,7 +290,7 @@ pub fn config(subcommand: &ConfigSubCommand, parameters: &Option<String>, stdin:
278290
valid: true,
279291
reason: None,
280292
};
281-
let valid = match validate_config(&json_string) {
293+
let valid = match validate_config(&configurator.get_config()) {
282294
Ok(()) => {
283295
true
284296
},
@@ -349,11 +361,11 @@ pub fn config(subcommand: &ConfigSubCommand, parameters: &Option<String>, stdin:
349361
/// # Errors
350362
///
351363
/// * `DscError` - The error that occurred.
352-
pub fn validate_config(config: &str) -> Result<(), DscError> {
364+
pub fn validate_config(config: &Configuration) -> Result<(), DscError> {
353365
// first validate against the config schema
354366
debug!("Validating configuration against schema");
355367
let schema = serde_json::to_value(get_schema(DscType::Configuration))?;
356-
let config_value = serde_json::from_str(config)?;
368+
let config_value = serde_json::to_value(config)?;
357369
validate_json("Configuration", &schema, &config_value)?;
358370
let mut dsc = DscManager::new()?;
359371

dsc_lib/src/configure/mod.rs

+35-19
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ pub mod depends_on;
3131
pub mod parameters;
3232

3333
pub struct Configurator {
34-
config: String,
34+
json: String,
35+
config: Configuration,
3536
pub context: Context,
3637
discovery: Discovery,
3738
statement_parser: Statement,
@@ -200,14 +201,26 @@ impl Configurator {
200201
/// # Errors
201202
///
202203
/// This function will return an error if the configuration is invalid or the underlying discovery fails.
203-
pub fn new(config: &str) -> Result<Configurator, DscError> {
204+
pub fn new(json: &str) -> Result<Configurator, DscError> {
204205
let discovery = Discovery::new()?;
205-
Ok(Configurator {
206-
config: config.to_owned(),
206+
let mut config = Configurator {
207+
json: json.to_owned(),
208+
config: Configuration::new(),
207209
context: Context::new(),
208210
discovery,
209211
statement_parser: Statement::new()?,
210-
})
212+
};
213+
config.validate_config()?;
214+
Ok(config)
215+
}
216+
217+
/// Get the configuration.
218+
///
219+
/// # Returns
220+
///
221+
/// * `&Configuration` - The configuration.
222+
pub fn get_config(&self) -> &Configuration {
223+
&self.config
211224
}
212225

213226
/// Invoke the get operation on a resource.
@@ -220,9 +233,8 @@ impl Configurator {
220233
///
221234
/// This function will return an error if the underlying resource fails.
222235
pub fn invoke_get(&mut self) -> Result<ConfigurationGetResult, DscError> {
223-
let config = self.validate_config()?;
224236
let mut result = ConfigurationGetResult::new();
225-
let resources = get_resource_invocation_order(&config, &mut self.statement_parser, &self.context)?;
237+
let resources = get_resource_invocation_order(&self.config, &mut self.statement_parser, &self.context)?;
226238
let pb_span = get_progress_bar_span(resources.len() as u64)?;
227239
let pb_span_enter = pb_span.enter();
228240
for resource in resources {
@@ -279,9 +291,8 @@ impl Configurator {
279291
///
280292
/// This function will return an error if the underlying resource fails.
281293
pub fn invoke_set(&mut self, skip_test: bool) -> Result<ConfigurationSetResult, DscError> {
282-
let config = self.validate_config()?;
283294
let mut result = ConfigurationSetResult::new();
284-
let resources = get_resource_invocation_order(&config, &mut self.statement_parser, &self.context)?;
295+
let resources = get_resource_invocation_order(&self.config, &mut self.statement_parser, &self.context)?;
285296
let pb_span = get_progress_bar_span(resources.len() as u64)?;
286297
let pb_span_enter = pb_span.enter();
287298
for resource in resources {
@@ -388,9 +399,8 @@ impl Configurator {
388399
///
389400
/// This function will return an error if the underlying resource fails.
390401
pub fn invoke_test(&mut self) -> Result<ConfigurationTestResult, DscError> {
391-
let config = self.validate_config()?;
392402
let mut result = ConfigurationTestResult::new();
393-
let resources = get_resource_invocation_order(&config, &mut self.statement_parser, &self.context)?;
403+
let resources = get_resource_invocation_order(&self.config, &mut self.statement_parser, &self.context)?;
394404
let pb_span = get_progress_bar_span(resources.len() as u64)?;
395405
let pb_span_enter = pb_span.enter();
396406
for resource in resources {
@@ -443,14 +453,13 @@ impl Configurator {
443453
///
444454
/// This function will return an error if the underlying resource fails.
445455
pub fn invoke_export(&mut self) -> Result<ConfigurationExportResult, DscError> {
446-
let config = self.validate_config()?;
447-
448456
let mut result = ConfigurationExportResult::new();
449457
let mut conf = config_doc::Configuration::new();
450458

451-
let pb_span = get_progress_bar_span(config.resources.len() as u64)?;
459+
let pb_span = get_progress_bar_span(self.config.resources.len() as u64)?;
452460
let pb_span_enter = pb_span.enter();
453-
for resource in config.resources {
461+
let resources = self.config.resources.clone();
462+
for resource in &resources {
454463
Span::current().pb_inc(1);
455464
pb_span.pb_set_message(format!("Export '{}'", resource.name).as_str());
456465
let properties = self.invoke_property_expressions(&resource.properties)?;
@@ -480,7 +489,7 @@ impl Configurator {
480489
/// This function will return an error if the parameters are invalid.
481490
pub fn set_parameters(&mut self, parameters_input: &Option<Value>) -> Result<(), DscError> {
482491
// set default parameters first
483-
let config = serde_json::from_str::<Configuration>(self.config.as_str())?;
492+
let config = serde_json::from_str::<Configuration>(self.json.as_str())?;
484493
let Some(parameters) = &config.parameters else {
485494
if parameters_input.is_none() {
486495
debug!("No parameters defined in configuration and no parameters input");
@@ -534,6 +543,12 @@ impl Configurator {
534543
info!("Set parameter '{name}' to '{value}'");
535544
}
536545
self.context.parameters.insert(name.clone(), (value.clone(), constraint.parameter_type.clone()));
546+
// also update the configuration with the parameter value
547+
if let Some(parameters) = &mut self.config.parameters {
548+
if let Some(parameter) = parameters.get_mut(&name) {
549+
parameter.default_value = Some(value);
550+
}
551+
}
537552
}
538553
else {
539554
return Err(DscError::Validation(format!("Parameter '{name}' not defined in configuration")));
@@ -592,14 +607,15 @@ impl Configurator {
592607
Ok(())
593608
}
594609

595-
fn validate_config(&mut self) -> Result<Configuration, DscError> {
596-
let config: Configuration = serde_json::from_str(self.config.as_str())?;
610+
fn validate_config(&mut self) -> Result<(), DscError> {
611+
let config: Configuration = serde_json::from_str(self.json.as_str())?;
597612
check_security_context(&config.metadata)?;
598613

599614
// Perform discovery of resources used in config
600615
let required_resources = config.resources.iter().map(|p| p.resource_type.clone()).collect::<Vec<String>>();
601616
self.discovery.find_resources(&required_resources);
602-
Ok(config)
617+
self.config = config;
618+
Ok(())
603619
}
604620

605621
fn invoke_property_expressions(&mut self, properties: &Option<Map<String, Value>>) -> Result<Option<Map<String, Value>>, DscError> {

0 commit comments

Comments
 (0)