Skip to content

Commit

Permalink
Merge branch 'main' into issue_592
Browse files Browse the repository at this point in the history
  • Loading branch information
anmenaga authored Jan 18, 2025
2 parents 92ea9fb + e5ff855 commit fa41d0a
Show file tree
Hide file tree
Showing 5 changed files with 238 additions and 61 deletions.
5 changes: 5 additions & 0 deletions dsc/examples/osinfo.parameters.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"parameters": {
"osFamily": "macOS"
}
}
37 changes: 37 additions & 0 deletions dsc/examples/osinfo_parameters.dsc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"$schema": "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json",
"parameters": {
"osFamily": {
"type": "string",
"defaultValue": "[concat('Win','dows')]",
"allowedValues": [
"Windows",
"Linux",
"macOS"
]
}
},
"resources": [
{
"name": "os",
"type": "Microsoft/OSInfo",
"properties": {
"family": "[parameters('osFamily')]"
}
},
{
"name": "another os instance",
"type": "Microsoft/OSInfo",
"properties": {
"family": "macOS"
}
},
{
"name": "path",
"type": "Microsoft.DSC.Debug/Echo",
"properties": {
"output": "[envvar('PATH')]"
}
}
]
}
4 changes: 2 additions & 2 deletions dsc/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ fn ctrlc_handler() {

fn terminate_subprocesses(sys: &System, process: &Process) {
info!("{}: {:?} {}", t!("main.terminatingSubprocess"), process.name(), process.pid());
for subprocess in sys.processes().values().filter(|p| p.parent().map_or(false, |parent| parent == process.pid())) {
for subprocess in sys.processes().values().filter(|p| p.parent().is_some_and(|parent| parent == process.pid())) {
terminate_subprocesses(sys, subprocess);
}

Expand Down Expand Up @@ -159,7 +159,7 @@ fn check_store() {
};

// MS Store runs app using `sihost.exe`
if parent_process.name().to_ascii_lowercase() == "sihost.exe" || parent_process.name().to_ascii_lowercase() == "explorer.exe"{
if parent_process.name().eq_ignore_ascii_case("sihost.exe") || parent_process.name().eq_ignore_ascii_case("explorer.exe") {
eprintln!("{}", t!("main.storeMessage"));
// wait for keypress
let _ = io::stdin().read(&mut [0u8]).unwrap();
Expand Down
140 changes: 82 additions & 58 deletions dsc/src/resolve.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

use dsc_lib::configure::config_doc::Configuration;
use dsc_lib::util::parse_input_to_json;
use rust_i18n::t;
use schemars::JsonSchema;
Expand All @@ -14,14 +13,30 @@ use tracing::{debug, info};
use crate::util::DSC_CONFIG_ROOT;

#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
pub struct Include {
pub enum IncludeKind {
/// The path to the file to include. Path is relative to the file containing the include
/// and not allowed to reference parent directories. If a configuration document is used
/// instead of a file, then the path is relative to the current working directory.
#[serde(rename = "configurationFile")]
pub configuration_file: String,
ConfigurationFile(String),
#[serde(rename = "configurationContent")]
ConfigurationContent(String),
}

#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
pub enum IncludeParametersKind {
#[serde(rename = "parametersFile")]
pub parameters_file: Option<String>,
ParametersFile(String),
#[serde(rename = "parametersContent")]
ParametersContent(String),
}

#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
pub struct Include {
#[serde(flatten)]
pub configuration: IncludeKind,
#[serde(flatten)]
pub parameters: Option<IncludeParametersKind>,
}

/// Read the file specified in the Include input and return the content as a JSON string.
Expand Down Expand Up @@ -51,74 +66,83 @@ pub fn get_contents(input: &str) -> Result<(Option<String>, String), String> {
}
};

let include_path = normalize_path(Path::new(&include.configuration_file))?;
let config_json = match include.configuration {
IncludeKind::ConfigurationFile(file_path) => {
let include_path = normalize_path(Path::new(&file_path))?;

// read the file specified in the Include input
let mut buffer: Vec<u8> = Vec::new();
match File::open(&include_path) {
Ok(mut file) => {
match file.read_to_end(&mut buffer) {
Ok(_) => (),
// read the file specified in the Include input
let mut buffer: Vec<u8> = Vec::new();
match File::open(&include_path) {
Ok(mut file) => {
match file.read_to_end(&mut buffer) {
Ok(_) => (),
Err(err) => {
return Err(format!("{} '{include_path:?}': {err}", t!("resolve.failedToReadFile")));
}
}
},
Err(err) => {
return Err(format!("{} '{include_path:?}': {err}", t!("resolve.failedToReadFile")));
return Err(format!("{} '{include_path:?}': {err}", t!("resolve.failedToOpenFile")));
}
}
},
Err(err) => {
return Err(format!("{} '{include_path:?}': {err}", t!("resolve.failedToOpenFile")));
}
}
// convert the buffer to a string
let include_content = match String::from_utf8(buffer) {
Ok(input) => input,
Err(err) => {
return Err(format!("{} '{include_path:?}': {err}", t!("resolve.invalidFileContent")));
}
};
// convert the buffer to a string
let include_content = match String::from_utf8(buffer) {
Ok(input) => input,
Err(err) => {
return Err(format!("{} '{include_path:?}': {err}", t!("resolve.invalidFileContent")));
}
};

// try to deserialize the Include content as YAML first
let configuration: Configuration = match serde_yaml::from_str(&include_content) {
Ok(configuration) => configuration,
Err(_err) => {
// if that fails, try to deserialize it as JSON
match serde_json::from_str(&include_content) {
Ok(configuration) => configuration,
match parse_input_to_json(&include_content) {
Ok(json) => json,
Err(err) => {
return Err(format!("{} '{include_path:?}': {err}", t!("resolve.invalidFile")));
}
}
},
IncludeKind::ConfigurationContent(text) => {
match parse_input_to_json(&text) {
Ok(json) => json,
Err(err) => {
return Err(format!("{}: {err}", t!("resolve.invalidFile")));
}
}
}
};

// serialize the Configuration as JSON
let config_json = match serde_json::to_string(&configuration) {
Ok(json) => json,
Err(err) => {
return Err(format!("JSON: {err}"));
}
};

let parameters = if let Some(parameters_file) = include.parameters_file {
// combine the path with DSC_CONFIG_ROOT
let parameters_file = normalize_path(Path::new(&parameters_file))?;
info!("{} '{parameters_file:?}'", t!("resolve.resolvingParameters"));
match std::fs::read_to_string(&parameters_file) {
Ok(parameters) => {
let parameters_json = match parse_input_to_json(&parameters) {
Ok(json) => json,
Err(err) => {
return Err(format!("{} '{parameters_file:?}': {err}", t!("resolve.failedParseParametersFile")));
}
};
Some(parameters_json)
},
Err(err) => {
return Err(format!("{} '{parameters_file:?}': {err}", t!("resolve.failedResolveParametersFile")));
let parameters = match include.parameters {
Some(IncludeParametersKind::ParametersFile(file_path)) => {
// combine the path with DSC_CONFIG_ROOT
let parameters_file = normalize_path(Path::new(&file_path))?;
info!("{} '{parameters_file:?}'", t!("resolve.resolvingParameters"));
match std::fs::read_to_string(&parameters_file) {
Ok(parameters) => {
let parameters_json = match parse_input_to_json(&parameters) {
Ok(json) => json,
Err(err) => {
return Err(format!("{} '{parameters_file:?}': {err}", t!("resolve.failedParseParametersFile")));
}
};
Some(parameters_json)
},
Err(err) => {
return Err(format!("{} '{parameters_file:?}': {err}", t!("resolve.failedResolveParametersFile")));
}
}
},
Some(IncludeParametersKind::ParametersContent(text)) => {
let parameters_json = match parse_input_to_json(&text) {
Ok(json) => json,
Err(err) => {
return Err(format!("{}: {err}", t!("resolve.invalidParametersContent")));
}
};
Some(parameters_json)
},
None => {
debug!("{}", t!("resolve.noParameters"));
None
}
} else {
debug!("{}", t!("resolve.noParametersFile"));
None
};

Ok((parameters, config_json))
Expand Down
113 changes: 112 additions & 1 deletion dsc/tests/dsc_include.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,34 @@ Describe 'Include tests' {
$logPath = Join-Path $TestDrive 'stderr.log'
}

It 'Include config with default parameters' {
It 'Include invalid config file' {
$invalidConfig = @"
`$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json
properties:
- name: osinfo
type: Microsoft.DSC/Include
properties:
configurationFile: include/non-existing.dsc.yaml
"@

$invalidConfigPath = Join-Path $TestDrive 'invalid_config.dsc.yaml'
$invalidConfig | Set-Content -Path $invalidConfigPath

$config = @"
`$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json
resources:
- name: osinfo
type: Microsoft.DSC/Include
properties:
configurationFile: $invalidConfigPath
"@
$configPath = Join-Path $TestDrive 'config.dsc.yaml'
$config | Set-Content -Path $configPath
dsc config get -f $configPath
$LASTEXITCODE | Should -Be 2
}

It 'Include config file with default parameters' {
$config = @"
`$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json
resources:
Expand All @@ -35,6 +62,63 @@ Describe 'Include tests' {
$out.results[0].result[0].result.actualState.family | Should -Be $expectedOS
}

It 'Include config YAML content with default parameters' {
# since this is YAML, we need to ensure correct indentation
$includeContent = (Get-Content $osinfoConfigPath -Raw).Replace("`n", "`n" + (' ' * 20))

$config = @"
`$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json
resources:
- name: osinfo
type: Microsoft.DSC/Include
properties:
configurationContent: |
$includeContent
"@

$configPath = Join-Path $TestDrive 'config.dsc.yaml'
$config | Set-Content -Path $configPath
$out = dsc config get -f $configPath | ConvertFrom-Json
$LASTEXITCODE | Should -Be 0
if ($IsWindows) {
$expectedOS = 'Windows'
} elseif ($IsLinux) {
$expectedOS = 'Linux'
} else {
$expectedOS = 'macOS'
}
$out.results[0].result[0].result.actualState.family | Should -Be $expectedOS
}

It 'Include config JSON content with default parameters' {
$osinfoJsonPath = Join-Path $PSScriptRoot '../examples/osinfo_parameters.dsc.json'

# for JSON, we can just have it as a single line
$includeContent = (Get-Content $osinfoJsonPath -Raw).Replace("`n", "").Replace('"', '\"')

$config = @"
`$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json
resources:
- name: osinfo
type: Microsoft.DSC/Include
properties:
configurationContent: "$includeContent"
"@

$configPath = Join-Path $TestDrive 'config.dsc.yaml'
$config | Set-Content -Path $configPath
$out = dsc config get -f $configPath | ConvertFrom-Json
$LASTEXITCODE | Should -Be 0
if ($IsWindows) {
$expectedOS = 'Windows'
} elseif ($IsLinux) {
$expectedOS = 'Linux'
} else {
$expectedOS = 'macOS'
}
$out.results[0].result[0].result.actualState.family | Should -Be $expectedOS
}

It 'Include config with parameters file' {
$config = @"
`$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json
Expand All @@ -59,6 +143,33 @@ Describe 'Include tests' {
$out.results[0].result[0].result.actualState.family | Should -Be $expectedOS
}

It 'Include config with parameters content' {
$parametersContentFile = Join-Path $PSScriptRoot '../examples/osinfo.parameters.json'
$parametersContent = (Get-Content $parametersContentFile -Raw).Replace("`n", "").Replace('"', '\"')

$config = @"
`$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json
resources:
- name: osinfo
type: Microsoft.DSC/Include
properties:
configurationFile: include/osinfo_parameters.dsc.yaml
parametersContent: "$parametersContent"
"@
$configPath = Join-Path $TestDrive 'config.dsc.yaml'
$config | Set-Content -Path $configPath
$out = dsc config get -f $configPath | ConvertFrom-Json
$LASTEXITCODE | Should -Be 0
if ($IsWindows) {
$expectedOS = 'Windows'
} elseif ($IsLinux) {
$expectedOS = 'Linux'
} else {
$expectedOS = 'macOS'
}
$out.results[0].result[0].result.actualState.family | Should -Be $expectedOS
}

It 'Invalid file path: <test>' -TestCases @(
@{ test = 'non-existing configuration'; config = 'include/non-existing.dsc.yaml'; parameters = $null }
@{ test = 'non-existing parameters'; config = 'include/osinfo_parameters.dsc.yaml'; parameters = 'include/non-existing.parameters.yaml' }
Expand Down

0 comments on commit fa41d0a

Please sign in to comment.