Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions dsc/tests/dsc_resource_condition.tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

Describe 'Resource condition tests' {
BeforeAll {
$configYaml = @'
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: test
type: Microsoft.DSC.Debug/Echo
condition: "[equals('skip', 'yes')]"
properties:
output: "This should not be executed"
- name: test2
type: Microsoft.DSC.Debug/Echo
condition: "[equals('no', 'no')]"
properties:
output: "This should be executed"
'@
}

It 'resource should be skipped for <operation>' -TestCases @(
@{ operation = 'get'; property = 'actualState' },
@{ operation = 'set'; property = 'afterState' },
@{ operation = 'test'; property = 'actualState' }
) {
param($operation, $property)
$out = dsc config $operation -i $configYaml 2>$TestDrive/error.log | ConvertFrom-Json
$LASTEXITCODE | Should -Be 0 -Because (Get-Content $TestDrive/error.log -Raw | Out-String)
$out.results.count | Should -Be 1
$out.results[0].result.$property.Output | Should -BeExactly "This should be executed"
}

It 'resource should be skipped for export' {
$out = dsc config export -i $configYaml 2>$TestDrive/error.log | ConvertFrom-Json
$LASTEXITCODE | Should -Be 0 -Because (Get-Content $TestDrive/error.log -Raw | Out-String)
$out.resources.count | Should -Be 1
$out.resources[0].type | Should -BeExactly 'Microsoft.DSC.Debug/Echo'
$out.resources[0].properties.output | Should -BeExactly "This should be executed"
}
}
1 change: 1 addition & 0 deletions dsc_lib/locales/en-us.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ _version = 1
[configure.config_doc]
configurationDocumentSchemaTitle = "Configuration document schema URI"
configurationDocumentSchemaDescription = "Defines the JSON Schema the configuration document adheres to."
skippingResource = "Skipping resource '%{name}' due to condition '%{condition}' with result '%{result}'"

[configure.constraints]
minLengthIsNull = "Parameter '%{name}' has minimum length constraint but is null"
Expand Down
3 changes: 3 additions & 0 deletions dsc_lib/src/configure/config_doc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,8 @@ pub struct Resource {
pub properties: Option<Map<String, Value>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub metadata: Option<Metadata>,
#[serde(skip_serializing_if = "Option::is_none")]
pub condition: Option<String>,
}

impl Default for Configuration {
Expand Down Expand Up @@ -217,6 +219,7 @@ impl Resource {
kind: None,
properties: None,
metadata: None,
condition: None,
}
}
}
Expand Down
27 changes: 27 additions & 0 deletions dsc_lib/src/configure/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,10 @@ impl Configurator {
for resource in resources {
progress.set_resource(&resource.name, &resource.resource_type);
progress.write_activity(format!("Get '{}'", resource.name).as_str());
if self.skip_resource(&resource)? {
progress.write_increment(1);
continue;
}
let Some(dsc_resource) = discovery.find_resource(&resource.resource_type) else {
return Err(DscError::ResourceNotFound(resource.resource_type));
};
Expand Down Expand Up @@ -387,6 +391,10 @@ impl Configurator {
for resource in resources {
progress.set_resource(&resource.name, &resource.resource_type);
progress.write_activity(format!("Set '{}'", resource.name).as_str());
if self.skip_resource(&resource)? {
progress.write_increment(1);
continue;
}
let Some(dsc_resource) = discovery.find_resource(&resource.resource_type) else {
return Err(DscError::ResourceNotFound(resource.resource_type));
};
Expand Down Expand Up @@ -535,6 +543,10 @@ impl Configurator {
for resource in resources {
progress.set_resource(&resource.name, &resource.resource_type);
progress.write_activity(format!("Test '{}'", resource.name).as_str());
if self.skip_resource(&resource)? {
progress.write_increment(1);
continue;
}
let Some(dsc_resource) = discovery.find_resource(&resource.resource_type) else {
return Err(DscError::ResourceNotFound(resource.resource_type));
};
Expand Down Expand Up @@ -608,6 +620,10 @@ impl Configurator {
for resource in &resources {
progress.set_resource(&resource.name, &resource.resource_type);
progress.write_activity(format!("Export '{}'", resource.name).as_str());
if self.skip_resource(resource)? {
progress.write_increment(1);
continue;
}
let Some(dsc_resource) = discovery.find_resource(&resource.resource_type) else {
return Err(DscError::ResourceNotFound(resource.resource_type.clone()));
};
Expand Down Expand Up @@ -642,6 +658,17 @@ impl Configurator {
Ok(result)
}

fn skip_resource(&mut self, resource: &Resource) -> Result<bool, DscError> {
if let Some(condition) = &resource.condition {
let condition_result = self.statement_parser.parse_and_execute(condition, &self.context)?;
if condition_result != Value::Bool(true) {
info!("{}", t!("configure.config_doc.skippingResource", name = resource.name, condition = condition, result = condition_result));
return Ok(true);
}
}
Ok(false)
}

/// Set the mounted path for the configuration.
///
/// # Arguments
Expand Down
9 changes: 9 additions & 0 deletions dscecho/echo.dsc.resource.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@
}
]
},
"export": {
"executable": "dscecho",
"args": [
{
"jsonInputArg": "--input",
"mandatory": true
}
]
},
"schema": {
"command": {
"executable": "dscecho"
Expand Down
Loading