Skip to content

Commit 44e88f1

Browse files
committed
change include resource to group type resource
1 parent 86ed4a8 commit 44e88f1

File tree

6 files changed

+75
-69
lines changed

6 files changed

+75
-69
lines changed

dsc/include.dsc.resource.json

+5-3
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,13 @@
33
"type": "Microsoft.DSC/Include",
44
"version": "0.1.0",
55
"description": "Allows including a configuration file with optional parameter file.",
6-
"kind": "Group",
6+
"kind": "Import",
77
"get": {
88
"executable": "dsc",
99
"args": [
1010
"config",
1111
"--as-include",
12+
"--as-group",
1213
"get"
1314
],
1415
"input": "stdin"
@@ -18,6 +19,7 @@
1819
"args": [
1920
"config",
2021
"--as-include",
22+
"--as-group",
2123
"set"
2224
],
2325
"input": "stdin",
@@ -29,10 +31,10 @@
2931
"args": [
3032
"config",
3133
"--as-include",
34+
"--as-group",
3235
"test"
3336
],
34-
"input": "stdin",
35-
"return": "state"
37+
"input": "stdin"
3638
},
3739
"exitCodes": {
3840
"0": "Success",

dsc/src/subcommand.rs

+25-15
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
// Licensed under the MIT License.
33

44
use crate::args::{ConfigSubCommand, DscType, OutputFormat, ResourceSubCommand};
5-
use crate::resolve::get_contents;
5+
use crate::resolve::{get_contents, Include};
66
use crate::resource_command::{get_resource, self};
77
use crate::Stream;
88
use crate::tablewriter::Table;
9-
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};
9+
use crate::util::{DSC_CONFIG_ROOT, EXIT_DSC_ERROR, EXIT_INVALID_INPUT, EXIT_JSON_ERROR, get_schema, write_output, get_input, set_dscconfigroot, validate_json};
1010
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;
@@ -285,31 +285,41 @@ pub fn config(subcommand: &ConfigSubCommand, parameters: &Option<String>, stdin:
285285
ConfigSubCommand::Test { format, as_get, .. } => {
286286
config_test(&mut configurator, format, as_group, as_get);
287287
},
288-
ConfigSubCommand::Validate { format, .. } => {
288+
ConfigSubCommand::Validate { document, path, format} => {
289289
let mut result = ValidateResult {
290290
valid: true,
291291
reason: None,
292292
};
293-
let valid = match validate_config(&configurator.get_config()) {
294-
Ok(()) => {
295-
true
296-
},
297-
Err(err) => {
298-
error!("{err}");
299-
result.valid = false;
300-
false
293+
if *as_include {
294+
let new_path = initialize_config_root(path);
295+
let input = get_input(document, stdin, &new_path);
296+
match serde_json::from_str::<Include>(&input) {
297+
Ok(_) => {
298+
// valid, so do nothing
299+
},
300+
Err(err) => {
301+
error!("Error: Failed to deserialize Include input: {err}");
302+
result.valid = false;
303+
}
301304
}
302-
};
305+
} else {
306+
match validate_config(configurator.get_config()) {
307+
Ok(()) => {
308+
// valid, so do nothing
309+
},
310+
Err(err) => {
311+
error!("{err}");
312+
result.valid = false;
313+
}
314+
};
315+
}
303316

304317
let Ok(json) = serde_json::to_string(&result) else {
305318
error!("Failed to convert validation result to JSON");
306319
exit(EXIT_JSON_ERROR);
307320
};
308321

309322
write_output(&json, format);
310-
if !valid {
311-
exit(EXIT_VALIDATION_FAILED);
312-
}
313323
},
314324
ConfigSubCommand::Export { format, .. } => {
315325
config_export(&mut configurator, format);

dsc/tests/dsc_include.tests.ps1

+36-6
Original file line numberDiff line numberDiff line change
@@ -170,9 +170,9 @@ resources:
170170
$out.results[0].result[0].result.actualState.output | Should -Be 'one'
171171
$out.results[1].result[0].name | Should -Be 'nested'
172172
$out.results[1].result[0].type | Should -Be 'Microsoft.DSC/Include'
173-
$out.results[1].result[0].result[0].name | Should -Be 'one'
174-
$out.results[1].result[0].result[0].type | Should -Be 'Test/Echo'
175-
$out.results[1].result[0].result[0].result[0].actualState.output | Should -Be 'one'
173+
$out.results[1].result[0].result.actualState.name | Should -Be 'one'
174+
$out.results[1].result[0].result.actualState.type | Should -Be 'Test/Echo'
175+
$out.results[1].result[0].result.actualState.result.actualState.output | Should -Be 'one'
176176
}
177177

178178
It 'Set with include works' {
@@ -203,9 +203,39 @@ resources:
203203

204204
$out = dsc config set -d $includeConfig | ConvertFrom-Json
205205
$LASTEXITCODE | Should -Be 0
206-
$out.results[0].result[0].name | Should -Be 'one'
207-
$out.results[0].result[0].type | Should -Be 'Test/Echo'
208-
$out.results[0].result[0].result.afterState.output | Should -Be 'Hello World'
206+
$out.results[0].result.beforeState[0].name | Should -Be 'one'
207+
$out.results[0].result.beforeState[0].type | Should -Be 'Test/Echo'
208+
$out.results[0].result.afterState[0].result.afterState.output | Should -Be 'Hello World'
209209
$out.hadErrors | Should -Be $false
210210
}
211+
212+
It 'Test with include works' {
213+
$includeYaml = Join-Path $PSScriptRoot ../../dsc/examples/include.dsc.yaml
214+
$out = dsc config test -p $includeYaml | ConvertFrom-Json
215+
$LASTEXITCODE | Should -Be 0
216+
$out.results[0].type | Should -BeExactly 'Microsoft.DSC/Include'
217+
$out.results[0].result[0].name | Should -BeExactly 'os'
218+
$out.results[0].result[0].type | Should -BeExactly 'Microsoft/OSInfo'
219+
$out.results[0].result[0].result.desiredState.family | Should -BeExactly 'macOS'
220+
221+
$family = if ($isWindows) {
222+
'Windows'
223+
} elseif ($IsLinux) {
224+
'Linux'
225+
} elseif ($IsMacOS) {
226+
'macOS'
227+
} else {
228+
'Unknown'
229+
}
230+
231+
$out.results[0].result[0].result.actualState.family | Should -BeExactly $family
232+
$expectedState = if ($IsMacOS) {
233+
$true
234+
} else {
235+
$false
236+
}
237+
238+
$out.results[0].result[0].result.inDesiredState | Should -Be $expectedState
239+
$out.results[0].result[0].result.differingProperties.Count | Should -Be 0
240+
}
211241
}

dsc_lib/src/configure/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,7 @@ impl Configurator {
219219
/// # Returns
220220
///
221221
/// * `&Configuration` - The configuration.
222+
#[must_use]
222223
pub fn get_config(&self) -> &Configuration {
223224
&self.config
224225
}

dsc_lib/src/dscresources/command_resource.rs

+7-36
Original file line numberDiff line numberDiff line change
@@ -4,30 +4,13 @@
44
use jsonschema::JSONSchema;
55
use serde_json::Value;
66
use std::{collections::HashMap, env, io::{Read, Write}, process::{Command, Stdio}};
7-
use crate::{configure::{config_doc::ExecutionKind, {config_result::ResourceGetResult, parameters, Configurator}}, util::parse_input_to_json};
7+
use crate::configure::{config_doc::ExecutionKind, config_result::{ResourceGetResult, ResourceTestResult}};
88
use crate::dscerror::DscError;
99
use super::{dscresource::get_diff, invoke_result::{ExportResult, GetResult, ResolveResult, SetResult, TestResult, ValidateResult, ResourceGetResponse, ResourceSetResponse, ResourceTestResponse, get_in_desired_state}, resource_manifest::{ArgKind, InputKind, Kind, ResourceManifest, ReturnKind, SchemaKind}};
1010
use tracing::{error, warn, info, debug, trace};
1111

1212
pub const EXIT_PROCESS_TERMINATED: i32 = 0x102;
1313

14-
fn get_configurator(resource: &ResourceManifest, cwd: &str, filter: &str) -> Result<Configurator, DscError> {
15-
let resolve_result = invoke_resolve(resource, cwd, filter)?;
16-
let configuration = serde_json::to_string(&resolve_result.configuration)?;
17-
let configuration_json = parse_input_to_json(&configuration)?;
18-
let mut configurator = Configurator::new(&configuration_json)?;
19-
let parameters = if let Some(parameters) = resolve_result.parameters {
20-
let parameters_input = parameters::Input {
21-
parameters,
22-
};
23-
Some(serde_json::to_value(parameters_input)?)
24-
} else {
25-
None
26-
};
27-
configurator.set_parameters(&parameters)?;
28-
Ok(configurator)
29-
}
30-
3114
/// Invoke the get operation on a resource
3215
///
3316
/// # Arguments
@@ -40,12 +23,6 @@ fn get_configurator(resource: &ResourceManifest, cwd: &str, filter: &str) -> Res
4023
/// Error returned if the resource does not successfully get the current state
4124
pub fn invoke_get(resource: &ResourceManifest, cwd: &str, filter: &str) -> Result<GetResult, DscError> {
4225
debug!("Invoking get for '{}'", &resource.resource_type);
43-
if resource.kind == Some(Kind::Import) {
44-
let mut configurator = get_configurator(resource, cwd, filter)?;
45-
let config_result = configurator.invoke_get()?;
46-
return Ok(GetResult::Group(config_result.results));
47-
}
48-
4926
let mut command_input = CommandInput { env: None, stdin: None };
5027
let Some(get) = &resource.get else {
5128
return Err(DscError::NotImplemented("get".to_string()));
@@ -95,12 +72,6 @@ pub fn invoke_get(resource: &ResourceManifest, cwd: &str, filter: &str) -> Resul
9572
#[allow(clippy::too_many_lines)]
9673
pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_test: bool, execution_type: &ExecutionKind) -> Result<SetResult, DscError> {
9774
debug!("Invoking set for '{}'", &resource.resource_type);
98-
if resource.kind == Some(Kind::Import) {
99-
let mut configurator = get_configurator(resource, cwd, desired)?;
100-
let config_result = configurator.invoke_set(skip_test)?;
101-
return Ok(SetResult::Group(config_result.results));
102-
}
103-
10475
let operation_type: String;
10576
let mut is_synthetic_what_if = false;
10677
let set_method = match execution_type {
@@ -275,12 +246,6 @@ pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_te
275246
/// Error is returned if the underlying command returns a non-zero exit code.
276247
pub fn invoke_test(resource: &ResourceManifest, cwd: &str, expected: &str) -> Result<TestResult, DscError> {
277248
debug!("Invoking test for '{}'", &resource.resource_type);
278-
if resource.kind == Some(Kind::Import) {
279-
let mut configurator = get_configurator(resource, cwd, expected)?;
280-
let config_result = configurator.invoke_test()?;
281-
return Ok(TestResult::Group(config_result.results));
282-
}
283-
284249
let Some(test) = &resource.test else {
285250
info!("Resource '{}' does not implement test, performing synthetic test", &resource.resource_type);
286251
return invoke_synthetic_test(resource, cwd, expected);
@@ -299,6 +264,12 @@ pub fn invoke_test(resource: &ResourceManifest, cwd: &str, expected: &str) -> Re
299264
verify_json(resource, cwd, &stdout)?;
300265
}
301266

267+
if resource.kind == Some(Kind::Import) {
268+
debug!("Import resource kind, returning group test response");
269+
let group_test_response: Vec<ResourceTestResult> = serde_json::from_str(&stdout)?;
270+
return Ok(TestResult::Group(group_test_response));
271+
}
272+
302273
let expected_value: Value = serde_json::from_str(expected)?;
303274
match test.returns {
304275
Some(ReturnKind::State) => {

dsc_lib/src/dscresources/dscresource.rs

+1-9
Original file line numberDiff line numberDiff line change
@@ -234,15 +234,7 @@ impl Invoke for DscResource {
234234
let resource_manifest = import_manifest(manifest.clone())?;
235235
if resource_manifest.test.is_none() {
236236
let get_result = self.get(expected)?;
237-
let desired_state = if self.kind == Kind::Import {
238-
let config = self.resolve(expected)?.configuration;
239-
// TODO: implement way to resolve entire config doc including expressions and parameters
240-
// as the raw configuration (desired state) won't match the result, also convert the desired
241-
// state to a TestResult so the comparison is consistent
242-
serde_json::to_value(config["resources"].clone())?
243-
} else {
244-
serde_json::from_str(expected)?
245-
};
237+
let desired_state = serde_json::from_str(expected)?;
246238
let actual_state = match get_result {
247239
GetResult::Group(results) => {
248240
let mut result_array: Vec<Value> = Vec::new();

0 commit comments

Comments
 (0)