From 15e04f70fc80ae3c8fd8a628cf2fe7d07bf982b8 Mon Sep 17 00:00:00 2001 From: "Steve Lee (POWERSHELL HE/HIM) (from Dev Box)" Date: Mon, 28 Oct 2024 21:25:36 -0700 Subject: [PATCH 01/15] Add `--mounted-path` parameter to dsc --- dsc/src/args.rs | 2 + dsc/src/main.rs | 6 +-- dsc/src/subcommand.rs | 6 ++- dsc/tests/dsc_functions.tests.ps1 | 27 +++++++++++ dsc_lib/src/configure/context.rs | 2 + dsc_lib/src/configure/mod.rs | 9 ++++ dsc_lib/src/functions/mod.rs | 2 + dsc_lib/src/functions/mounted_path.rs | 66 +++++++++++++++++++++++++++ 8 files changed, 116 insertions(+), 4 deletions(-) create mode 100644 dsc_lib/src/functions/mounted_path.rs diff --git a/dsc/src/args.rs b/dsc/src/args.rs index e572ca31..83cf4c1a 100644 --- a/dsc/src/args.rs +++ b/dsc/src/args.rs @@ -48,6 +48,8 @@ pub enum SubCommand { parameters: Option, #[clap(short = 'f', long, help = "Parameters to pass to the configuration as a JSON or YAML file", conflicts_with = "parameters")] parameters_file: Option, + #[clap(short = 'm', long, help = "Mounted path to use for supported resources")] + mounted_path: Option, // Used to inform when DSC is used as a group resource to modify it's output #[clap(long, hide = true)] as_group: bool, diff --git a/dsc/src/main.rs b/dsc/src/main.rs index 77adaa4c..547f68b8 100644 --- a/dsc/src/main.rs +++ b/dsc/src/main.rs @@ -68,11 +68,11 @@ fn main() { let mut cmd = Args::command(); generate(shell, &mut cmd, "dsc", &mut io::stdout()); }, - SubCommand::Config { subcommand, parameters, parameters_file, as_group, as_include } => { + SubCommand::Config { subcommand, parameters, parameters_file, mounted_path, as_group, as_include } => { if let Some(file_name) = parameters_file { info!("Reading parameters from file {file_name}"); match std::fs::read_to_string(&file_name) { - Ok(parameters) => subcommand::config(&subcommand, &Some(parameters), &input, &as_group, &as_include), + Ok(parameters) => subcommand::config(&subcommand, &Some(parameters), &mounted_path, &input, &as_group, &as_include), Err(err) => { error!("Error: Failed to read parameters file '{file_name}': {err}"); exit(util::EXIT_INVALID_INPUT); @@ -80,7 +80,7 @@ fn main() { } } else { - subcommand::config(&subcommand, ¶meters, &input, &as_group, &as_include); + subcommand::config(&subcommand, ¶meters, &mounted_path, &input, &as_group, &as_include); } }, SubCommand::Resource { subcommand } => { diff --git a/dsc/src/subcommand.rs b/dsc/src/subcommand.rs index 4b1783fe..29b9d15d 100644 --- a/dsc/src/subcommand.rs +++ b/dsc/src/subcommand.rs @@ -186,7 +186,7 @@ fn initialize_config_root(path: &Option) -> Option { } #[allow(clippy::too_many_lines)] -pub fn config(subcommand: &ConfigSubCommand, parameters: &Option, stdin: &Option, as_group: &bool, as_include: &bool) { +pub fn config(subcommand: &ConfigSubCommand, parameters: &Option, mounted_path: &Option, stdin: &Option, as_group: &bool, as_include: &bool) { let (new_parameters, json_string) = match subcommand { ConfigSubCommand::Get { document, path, .. } | ConfigSubCommand::Set { document, path, .. } | @@ -270,6 +270,10 @@ pub fn config(subcommand: &ConfigSubCommand, parameters: &Option, stdin: } }; + if let Some(path) = mounted_path { + configurator.set_mounted_path(path); + } + if let Err(err) = configurator.set_context(¶meters) { error!("Error: Parameter input failure: {err}"); exit(EXIT_INVALID_INPUT); diff --git a/dsc/tests/dsc_functions.tests.ps1 b/dsc/tests/dsc_functions.tests.ps1 index 53db473a..9d6b3e10 100644 --- a/dsc/tests/dsc_functions.tests.ps1 +++ b/dsc/tests/dsc_functions.tests.ps1 @@ -24,4 +24,31 @@ Describe 'tests for function expressions' { $out = $config_yaml | dsc config get | ConvertFrom-Json $out.results[0].result.actualState.output | Should -Be $expected } + + It 'mountedpath() works' -TestCases @( + @{ path = '' } + @{ path = "hello$([System.IO.Path]::DirectorySeparatorChar)world" } + ) { + param($path) + + $testPath = if ($path.Length -gt 0) { + $expected = "$PSHOME$([System.IO.Path]::DirectorySeparatorChar)$path" + "'$($path.replace('\', '\\'))'" + } + else { + $expected = $PSHOME + $path + } + + $config_yaml = @" + `$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json + resources: + - name: Echo + type: Microsoft.DSC.Debug/Echo + properties: + output: "[mountedpath($testPath)]" +"@ + $out = $config_yaml | dsc config --mounted-path $PSHOME get | ConvertFrom-Json + $out.results[0].result.actualState.output | Should -BeExactly $expected + } } diff --git a/dsc_lib/src/configure/context.rs b/dsc_lib/src/configure/context.rs index 0ce678fe..2e3494b7 100644 --- a/dsc_lib/src/configure/context.rs +++ b/dsc_lib/src/configure/context.rs @@ -12,6 +12,7 @@ use super::config_doc::{DataType, SecurityContextKind}; pub struct Context { pub execution_type: ExecutionKind, pub outputs: HashMap, // this is used by the `reference()` function to retrieve output + pub mounted_path: String, pub parameters: HashMap, pub security_context: SecurityContextKind, pub variables: HashMap, @@ -24,6 +25,7 @@ impl Context { Self { execution_type: ExecutionKind::Actual, outputs: HashMap::new(), + mounted_path: String::new(), parameters: HashMap::new(), security_context: match get_security_context() { SecurityContext::Admin => SecurityContextKind::Elevated, diff --git a/dsc_lib/src/configure/mod.rs b/dsc_lib/src/configure/mod.rs index 3a5d84a3..d8f5dffa 100644 --- a/dsc_lib/src/configure/mod.rs +++ b/dsc_lib/src/configure/mod.rs @@ -479,6 +479,15 @@ impl Configurator { Ok(result) } + /// Set the mounted path for the configuration. + /// + /// # Arguments + /// + /// * `mounted_path` - The mounted path to set. + pub fn set_mounted_path(&mut self, mounted_path: &str) { + self.context.mounted_path = mounted_path.to_string(); + } + /// Set the parameters and variables context for the configuration. /// /// # Arguments diff --git a/dsc_lib/src/functions/mod.rs b/dsc_lib/src/functions/mod.rs index 5d631877..ea873adb 100644 --- a/dsc_lib/src/functions/mod.rs +++ b/dsc_lib/src/functions/mod.rs @@ -17,6 +17,7 @@ pub mod int; pub mod max; pub mod min; pub mod mod_function; +pub mod mounted_path; pub mod mul; pub mod parameters; pub mod reference; @@ -74,6 +75,7 @@ impl FunctionDispatcher { functions.insert("max".to_string(), Box::new(max::Max{})); functions.insert("min".to_string(), Box::new(min::Min{})); functions.insert("mod".to_string(), Box::new(mod_function::Mod{})); + functions.insert("mountedpath".to_string(), Box::new(mounted_path::MountedPath{})); functions.insert("mul".to_string(), Box::new(mul::Mul{})); functions.insert("parameters".to_string(), Box::new(parameters::Parameters{})); functions.insert("reference".to_string(), Box::new(reference::Reference{})); diff --git a/dsc_lib/src/functions/mounted_path.rs b/dsc_lib/src/functions/mounted_path.rs new file mode 100644 index 00000000..618340b1 --- /dev/null +++ b/dsc_lib/src/functions/mounted_path.rs @@ -0,0 +1,66 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::DscError; +use crate::configure::context::Context; +use crate::functions::{AcceptedArgKind, Function}; +use serde_json::Value; +use tracing::debug; + +#[derive(Debug, Default)] +pub struct MountedPath {} + +/// Implements the 'mountedpath' function. +/// This function returns the value of the mounted path. +/// The optional parameter is a path appended to the mounted path. +/// Path is not validated as it might be used for creation. +impl Function for MountedPath { + fn min_args(&self) -> usize { + 0 + } + + fn max_args(&self) -> usize { + 1 + } + + fn accepted_arg_types(&self) -> Vec { + vec![AcceptedArgKind::String] + } + + fn invoke(&self, args: &[Value], context: &Context) -> Result { + debug!("Executing mountedpath function with args: {:?}", args); + + if args.len() == 1 { + let path = args[0].as_str().ok_or(DscError::Parser("Invalid argument".to_string()))?; + Ok(Value::String(format!("{}{}{path}", context.mounted_path, std::path::MAIN_SEPARATOR))) + } else { + Ok(Value::String(context.mounted_path.clone())) + } + } +} + +#[cfg(test)] +mod tests { + use crate::configure::context::Context; + use crate::parser::Statement; + + #[test] + fn no_arg() { + let mut parser = Statement::new().unwrap(); + let mut context = Context::new(); + let separator = std::path::MAIN_SEPARATOR; + context.mounted_path = format!("{separator}mnt"); + let result = parser.parse_and_execute("[mountedpath()]", &context).unwrap(); + assert_eq!(result, format!("{separator}mnt")); + } + + #[test] + fn with_arg() { + let mut parser = Statement::new().unwrap(); + let mut context = Context::new(); + let separator = std::path::MAIN_SEPARATOR; + context.mounted_path = format!("{separator}mnt"); + let result = parser.parse_and_execute("[mountedpath('foo')]", &context).unwrap(); + assert_eq!(result, format!("{separator}mnt{separator}foo")); + } +} From ab7d49116975162d403355b71bb1d2a05bf0bfbc Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Tue, 29 Oct 2024 15:38:18 -0700 Subject: [PATCH 02/15] change to `--target-path` and `targetPath()`. add `path()` function --- .vscode/settings.json | 2 +- dsc/src/args.rs | 4 +- dsc/src/main.rs | 6 +- dsc/src/subcommand.rs | 18 +++-- dsc/tests/dsc_args.tests.ps1 | 6 ++ dsc/tests/dsc_functions.tests.ps1 | 26 +++---- dsc_lib/src/configure/context.rs | 6 +- dsc_lib/src/configure/mod.rs | 9 +-- dsc_lib/src/functions/mod.rs | 6 +- dsc_lib/src/functions/path.rs | 67 +++++++++++++++++++ .../{mounted_path.rs => target_path.rs} | 35 +++++----- 11 files changed, 130 insertions(+), 55 deletions(-) create mode 100644 dsc_lib/src/functions/path.rs rename dsc_lib/src/functions/{mounted_path.rs => target_path.rs} (53%) diff --git a/.vscode/settings.json b/.vscode/settings.json index 292ec34e..24bf5814 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,7 +2,7 @@ "rust-analyzer.linkedProjects": [ "./dsc/Cargo.toml", "./dsc_lib/Cargo.toml", - "./echo/Cargo.toml", + "./dscecho/Cargo.toml", "./osinfo/Cargo.toml", "./registry/Cargo.toml", "./runcommandonset/Cargo.toml", diff --git a/dsc/src/args.rs b/dsc/src/args.rs index 83cf4c1a..77edf478 100644 --- a/dsc/src/args.rs +++ b/dsc/src/args.rs @@ -48,8 +48,8 @@ pub enum SubCommand { parameters: Option, #[clap(short = 'f', long, help = "Parameters to pass to the configuration as a JSON or YAML file", conflicts_with = "parameters")] parameters_file: Option, - #[clap(short = 'm', long, help = "Mounted path to use for supported resources")] - mounted_path: Option, + #[clap(short = 't', long, help = "Target path to apply configuration, requires resource support.")] + target_path: Option, // Used to inform when DSC is used as a group resource to modify it's output #[clap(long, hide = true)] as_group: bool, diff --git a/dsc/src/main.rs b/dsc/src/main.rs index 547f68b8..31216d2c 100644 --- a/dsc/src/main.rs +++ b/dsc/src/main.rs @@ -68,11 +68,11 @@ fn main() { let mut cmd = Args::command(); generate(shell, &mut cmd, "dsc", &mut io::stdout()); }, - SubCommand::Config { subcommand, parameters, parameters_file, mounted_path, as_group, as_include } => { + SubCommand::Config { subcommand, parameters, parameters_file, target_path, as_group, as_include } => { if let Some(file_name) = parameters_file { info!("Reading parameters from file {file_name}"); match std::fs::read_to_string(&file_name) { - Ok(parameters) => subcommand::config(&subcommand, &Some(parameters), &mounted_path, &input, &as_group, &as_include), + Ok(parameters) => subcommand::config(&subcommand, &Some(parameters), &target_path, &input, &as_group, &as_include), Err(err) => { error!("Error: Failed to read parameters file '{file_name}': {err}"); exit(util::EXIT_INVALID_INPUT); @@ -80,7 +80,7 @@ fn main() { } } else { - subcommand::config(&subcommand, ¶meters, &mounted_path, &input, &as_group, &as_include); + subcommand::config(&subcommand, ¶meters, &target_path, &input, &as_group, &as_include); } }, SubCommand::Resource { subcommand } => { diff --git a/dsc/src/subcommand.rs b/dsc/src/subcommand.rs index 29b9d15d..4c6599a0 100644 --- a/dsc/src/subcommand.rs +++ b/dsc/src/subcommand.rs @@ -5,7 +5,7 @@ use crate::args::{ConfigSubCommand, DscType, OutputFormat, ResourceSubCommand}; use crate::resolve::{get_contents, Include}; use crate::resource_command::{get_resource, self}; use crate::tablewriter::Table; -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}; +use crate::util::{DSC_CONFIG_ROOT, EXIT_DSC_ERROR, EXIT_INVALID_ARGS, EXIT_INVALID_INPUT, EXIT_JSON_ERROR, get_schema, write_output, get_input, set_dscconfigroot, validate_json}; use dsc_lib::configure::{Configurator, config_doc::{Configuration, ExecutionKind}, config_result::ResourceGetResult}; use dsc_lib::dscerror::DscError; use dsc_lib::dscresources::invoke_result::ResolveResult; @@ -15,9 +15,12 @@ use dsc_lib::{ dscresources::dscresource::{Capability, ImplementedAs, Invoke}, dscresources::resource_manifest::{import_manifest, ResourceManifest}, }; -use std::collections::HashMap; -use std::io::{self, IsTerminal}; -use std::process::exit; +use std::{ + collections::HashMap, + io::{self, IsTerminal}, + path::Path, + process::exit +}; use tracing::{debug, error, trace}; pub fn config_get(configurator: &mut Configurator, format: &Option, as_group: &bool) @@ -271,7 +274,12 @@ pub fn config(subcommand: &ConfigSubCommand, parameters: &Option, mounte }; if let Some(path) = mounted_path { - configurator.set_mounted_path(path); + if !Path::new(&path).exists() { + error!("Error: Target path '{path}' does not exist"); + exit(EXIT_INVALID_ARGS); + } + + configurator.set_target_path(path); } if let Err(err) = configurator.set_context(¶meters) { diff --git a/dsc/tests/dsc_args.tests.ps1 b/dsc/tests/dsc_args.tests.ps1 index 70752a40..a63c438b 100644 --- a/dsc/tests/dsc_args.tests.ps1 +++ b/dsc/tests/dsc_args.tests.ps1 @@ -308,4 +308,10 @@ resources: $LASTEXITCODE | Should -Be 2 "$TestDrive/tracing.txt" | Should -FileContentMatchExactly 'Can not perform this operation on the adapter' } + + It 'Invalid --target-path' { + dsc config --target-path /invalid/path get -p "$PSScriptRoot/../examples/groups.dsc.yaml" 2> $TestDrive/tracing.txt + $LASTEXITCODE | Should -Be 1 + "$TestDrive/tracing.txt" | Should -FileContentMatchExactly "Target path '/invalid/path' does not exist" + } } diff --git a/dsc/tests/dsc_functions.tests.ps1 b/dsc/tests/dsc_functions.tests.ps1 index 9d6b3e10..c4064e88 100644 --- a/dsc/tests/dsc_functions.tests.ps1 +++ b/dsc/tests/dsc_functions.tests.ps1 @@ -2,6 +2,11 @@ # Licensed under the MIT License. Describe 'tests for function expressions' { + BeforeAll { + $global:sep = [System.IO.Path]::DirectorySeparatorChar + } + + It 'function works: ' -TestCases @( @{ text = "[concat('a', 'b')]"; expected = 'ab' } @{ text = "[concat('a', 'b', 'c')]"; expected = 'abc' } @@ -25,20 +30,11 @@ Describe 'tests for function expressions' { $out.results[0].result.actualState.output | Should -Be $expected } - It 'mountedpath() works' -TestCases @( - @{ path = '' } - @{ path = "hello$([System.IO.Path]::DirectorySeparatorChar)world" } + It 'path() works' -TestCases @( + @{ path = "targetPath(), 'a'"; expected = "$PSHOME${sep}a" } + @{ path = "'a', 'b', 'c'"; expected = "a${sep}b${sep}c" } ) { - param($path) - - $testPath = if ($path.Length -gt 0) { - $expected = "$PSHOME$([System.IO.Path]::DirectorySeparatorChar)$path" - "'$($path.replace('\', '\\'))'" - } - else { - $expected = $PSHOME - $path - } + param($path, $expected) $config_yaml = @" `$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json @@ -46,9 +42,9 @@ Describe 'tests for function expressions' { - name: Echo type: Microsoft.DSC.Debug/Echo properties: - output: "[mountedpath($testPath)]" + output: "[path($path)]" "@ - $out = $config_yaml | dsc config --mounted-path $PSHOME get | ConvertFrom-Json + $out = $config_yaml | dsc config --target-path $PSHOME get | ConvertFrom-Json $out.results[0].result.actualState.output | Should -BeExactly $expected } } diff --git a/dsc_lib/src/configure/context.rs b/dsc_lib/src/configure/context.rs index 2e3494b7..7263f4fe 100644 --- a/dsc_lib/src/configure/context.rs +++ b/dsc_lib/src/configure/context.rs @@ -5,14 +5,14 @@ use chrono::{DateTime, Local}; use crate::configure::config_doc::ExecutionKind; use security_context_lib::{get_security_context, SecurityContext}; use serde_json::Value; -use std::collections::HashMap; +use std::{collections::HashMap, path::PathBuf}; use super::config_doc::{DataType, SecurityContextKind}; pub struct Context { pub execution_type: ExecutionKind, pub outputs: HashMap, // this is used by the `reference()` function to retrieve output - pub mounted_path: String, + pub target_path: PathBuf, pub parameters: HashMap, pub security_context: SecurityContextKind, pub variables: HashMap, @@ -25,7 +25,7 @@ impl Context { Self { execution_type: ExecutionKind::Actual, outputs: HashMap::new(), - mounted_path: String::new(), + target_path: PathBuf::new(), parameters: HashMap::new(), security_context: match get_security_context() { SecurityContext::Admin => SecurityContextKind::Elevated, diff --git a/dsc_lib/src/configure/mod.rs b/dsc_lib/src/configure/mod.rs index d8f5dffa..4aed18c5 100644 --- a/dsc_lib/src/configure/mod.rs +++ b/dsc_lib/src/configure/mod.rs @@ -20,6 +20,7 @@ use self::contraints::{check_length, check_number_limits, check_allowed_values}; use indicatif::ProgressStyle; use security_context_lib::{SecurityContext, get_security_context}; use serde_json::{Map, Value}; +use std::path::PathBuf; use std::{collections::HashMap, mem}; use tracing::{debug, info, trace, warn_span, Span}; use tracing_indicatif::span_ext::IndicatifSpanExt; @@ -480,12 +481,12 @@ impl Configurator { } /// Set the mounted path for the configuration. - /// + /// /// # Arguments - /// + /// /// * `mounted_path` - The mounted path to set. - pub fn set_mounted_path(&mut self, mounted_path: &str) { - self.context.mounted_path = mounted_path.to_string(); + pub fn set_target_path(&mut self, target_path: &str) { + self.context.target_path = PathBuf::from(target_path); } /// Set the parameters and variables context for the configuration. diff --git a/dsc_lib/src/functions/mod.rs b/dsc_lib/src/functions/mod.rs index ea873adb..b6559e32 100644 --- a/dsc_lib/src/functions/mod.rs +++ b/dsc_lib/src/functions/mod.rs @@ -17,12 +17,13 @@ pub mod int; pub mod max; pub mod min; pub mod mod_function; -pub mod mounted_path; pub mod mul; pub mod parameters; +pub mod path; pub mod reference; pub mod resource_id; pub mod sub; +pub mod target_path; pub mod variables; /// The kind of argument that a function accepts. @@ -75,12 +76,13 @@ impl FunctionDispatcher { functions.insert("max".to_string(), Box::new(max::Max{})); functions.insert("min".to_string(), Box::new(min::Min{})); functions.insert("mod".to_string(), Box::new(mod_function::Mod{})); - functions.insert("mountedpath".to_string(), Box::new(mounted_path::MountedPath{})); functions.insert("mul".to_string(), Box::new(mul::Mul{})); functions.insert("parameters".to_string(), Box::new(parameters::Parameters{})); + functions.insert("path".to_string(), Box::new(path::Path{})); functions.insert("reference".to_string(), Box::new(reference::Reference{})); functions.insert("resourceId".to_string(), Box::new(resource_id::ResourceId{})); functions.insert("sub".to_string(), Box::new(sub::Sub{})); + functions.insert("targetPath".to_string(), Box::new(target_path::TargetPath{})); functions.insert("variables".to_string(), Box::new(variables::Variables{})); Self { functions, diff --git a/dsc_lib/src/functions/path.rs b/dsc_lib/src/functions/path.rs new file mode 100644 index 00000000..3e5877d5 --- /dev/null +++ b/dsc_lib/src/functions/path.rs @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +use crate::DscError; +use crate::configure::context::Context; +use crate::functions::{AcceptedArgKind, Function}; +use serde_json::Value; +use std::path::PathBuf; +use tracing::debug; + +#[derive(Debug, Default)] +pub struct Path {} + +/// Implements the 'mountedpath' function. +/// This function returns the value of the mounted path. +/// The optional parameter is a path appended to the mounted path. +/// Path is not validated as it might be used for creation. +impl Function for Path { + fn min_args(&self) -> usize { + 2 + } + + fn max_args(&self) -> usize { + usize::MAX + } + + fn accepted_arg_types(&self) -> Vec { + vec![AcceptedArgKind::String] + } + + fn invoke(&self, args: &[Value], _context: &Context) -> Result { + debug!("Executing path function with args: {:?}", args); + + let mut path = PathBuf::new(); + for arg in args { + if let Value::String(s) = arg { + path.push(s); + } else { + return Err(DscError::Parser("Arguments must all be strings".to_string())); + } + } + + Ok(Value::String(path.to_string_lossy().to_string())) + } +} + +#[cfg(test)] +mod tests { + use crate::configure::context::Context; + use crate::parser::Statement; + + #[test] + fn no_arg() { + let mut parser = Statement::new().unwrap(); + let separator = std::path::MAIN_SEPARATOR; + let result = parser.parse_and_execute("[path('a','b'", &Context::new()).unwrap(); + assert_eq!(result, format!("a{separator}b")); + } + + #[test] + fn with_arg() { + let mut parser = Statement::new().unwrap(); + let separator = std::path::MAIN_SEPARATOR; + let result = parser.parse_and_execute("[path('a','b','c')]", &Context::new()).unwrap(); + assert_eq!(result, format!("{separator}a{separator}b{separator}c")); + } +} diff --git a/dsc_lib/src/functions/mounted_path.rs b/dsc_lib/src/functions/target_path.rs similarity index 53% rename from dsc_lib/src/functions/mounted_path.rs rename to dsc_lib/src/functions/target_path.rs index 618340b1..79278ed6 100644 --- a/dsc_lib/src/functions/mounted_path.rs +++ b/dsc_lib/src/functions/target_path.rs @@ -8,34 +8,29 @@ use serde_json::Value; use tracing::debug; #[derive(Debug, Default)] -pub struct MountedPath {} +pub struct TargetPath {} /// Implements the 'mountedpath' function. /// This function returns the value of the mounted path. /// The optional parameter is a path appended to the mounted path. /// Path is not validated as it might be used for creation. -impl Function for MountedPath { +impl Function for TargetPath { fn min_args(&self) -> usize { 0 } fn max_args(&self) -> usize { - 1 + 0 } fn accepted_arg_types(&self) -> Vec { vec![AcceptedArgKind::String] } - fn invoke(&self, args: &[Value], context: &Context) -> Result { - debug!("Executing mountedpath function with args: {:?}", args); + fn invoke(&self, _args: &[Value], context: &Context) -> Result { + debug!("Executing targetPath function"); - if args.len() == 1 { - let path = args[0].as_str().ok_or(DscError::Parser("Invalid argument".to_string()))?; - Ok(Value::String(format!("{}{}{path}", context.mounted_path, std::path::MAIN_SEPARATOR))) - } else { - Ok(Value::String(context.mounted_path.clone())) - } + Ok(Value::String(context.target_path.to_string_lossy().to_string())) } } @@ -43,24 +38,24 @@ impl Function for MountedPath { mod tests { use crate::configure::context::Context; use crate::parser::Statement; + use std::path::PathBuf; #[test] - fn no_arg() { + fn init() { let mut parser = Statement::new().unwrap(); let mut context = Context::new(); - let separator = std::path::MAIN_SEPARATOR; - context.mounted_path = format!("{separator}mnt"); - let result = parser.parse_and_execute("[mountedpath()]", &context).unwrap(); - assert_eq!(result, format!("{separator}mnt")); + let result = parser.parse_and_execute("[targetPath()]", &context).unwrap(); + assert_eq!(result, ""); } #[test] - fn with_arg() { + fn simple() { let mut parser = Statement::new().unwrap(); let mut context = Context::new(); let separator = std::path::MAIN_SEPARATOR; - context.mounted_path = format!("{separator}mnt"); - let result = parser.parse_and_execute("[mountedpath('foo')]", &context).unwrap(); - assert_eq!(result, format!("{separator}mnt{separator}foo")); + context.target_path = PathBuf::from(format!("{separator}mnt")); + let result = parser.parse_and_execute("[targetPath()]", &context).unwrap(); + assert_eq!(result, format!("{separator}mnt")); } + } From 8195405d74ec8895fd0f793d2d24270795b162fb Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Tue, 29 Oct 2024 16:14:34 -0700 Subject: [PATCH 03/15] fix tests --- dsc_lib/src/functions/path.rs | 8 ++++---- dsc_lib/src/functions/target_path.rs | 3 +-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/dsc_lib/src/functions/path.rs b/dsc_lib/src/functions/path.rs index 3e5877d5..5d0c0f83 100644 --- a/dsc_lib/src/functions/path.rs +++ b/dsc_lib/src/functions/path.rs @@ -50,18 +50,18 @@ mod tests { use crate::parser::Statement; #[test] - fn no_arg() { + fn two_args() { let mut parser = Statement::new().unwrap(); let separator = std::path::MAIN_SEPARATOR; - let result = parser.parse_and_execute("[path('a','b'", &Context::new()).unwrap(); + let result = parser.parse_and_execute("[path('a','b')]", &Context::new()).unwrap(); assert_eq!(result, format!("a{separator}b")); } #[test] - fn with_arg() { + fn three_args() { let mut parser = Statement::new().unwrap(); let separator = std::path::MAIN_SEPARATOR; let result = parser.parse_and_execute("[path('a','b','c')]", &Context::new()).unwrap(); - assert_eq!(result, format!("{separator}a{separator}b{separator}c")); + assert_eq!(result, format!("a{separator}b{separator}c")); } } diff --git a/dsc_lib/src/functions/target_path.rs b/dsc_lib/src/functions/target_path.rs index 79278ed6..29ca90d0 100644 --- a/dsc_lib/src/functions/target_path.rs +++ b/dsc_lib/src/functions/target_path.rs @@ -43,8 +43,7 @@ mod tests { #[test] fn init() { let mut parser = Statement::new().unwrap(); - let mut context = Context::new(); - let result = parser.parse_and_execute("[targetPath()]", &context).unwrap(); + let result = parser.parse_and_execute("[targetPath()]", &Context::new()).unwrap(); assert_eq!(result, ""); } From ddbe82f1c1733fbee729c50d2b1d1123ec80f028 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Tue, 29 Oct 2024 16:28:52 -0700 Subject: [PATCH 04/15] update test --- dsc/tests/dsc_functions.tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dsc/tests/dsc_functions.tests.ps1 b/dsc/tests/dsc_functions.tests.ps1 index c4064e88..bd16cc5a 100644 --- a/dsc/tests/dsc_functions.tests.ps1 +++ b/dsc/tests/dsc_functions.tests.ps1 @@ -3,7 +3,7 @@ Describe 'tests for function expressions' { BeforeAll { - $global:sep = [System.IO.Path]::DirectorySeparatorChar + $sep = [System.IO.Path]::DirectorySeparatorChar } From b2a55283642bcdaba0259d9e8c63b7f816d62b24 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Tue, 29 Oct 2024 16:46:14 -0700 Subject: [PATCH 05/15] update test --- dsc/tests/dsc_functions.tests.ps1 | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/dsc/tests/dsc_functions.tests.ps1 b/dsc/tests/dsc_functions.tests.ps1 index bd16cc5a..7091daeb 100644 --- a/dsc/tests/dsc_functions.tests.ps1 +++ b/dsc/tests/dsc_functions.tests.ps1 @@ -2,11 +2,6 @@ # Licensed under the MIT License. Describe 'tests for function expressions' { - BeforeAll { - $sep = [System.IO.Path]::DirectorySeparatorChar - } - - It 'function works: ' -TestCases @( @{ text = "[concat('a', 'b')]"; expected = 'ab' } @{ text = "[concat('a', 'b', 'c')]"; expected = 'abc' } @@ -31,8 +26,8 @@ Describe 'tests for function expressions' { } It 'path() works' -TestCases @( - @{ path = "targetPath(), 'a'"; expected = "$PSHOME${sep}a" } - @{ path = "'a', 'b', 'c'"; expected = "a${sep}b${sep}c" } + @{ path = "targetPath(), 'a'"; expected = "$PSHOME$([System.IO.Path]::DirectorySeparatorChar)a" } + @{ path = "'a', 'b', 'c'"; expected = "a$([System.IO.Path]::DirectorySeparatorChar)b$([System.IO.Path]::DirectorySeparatorChar)c" } ) { param($path, $expected) From 7fd5060f8615302e3e1e209aadf9c2e638091c88 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Wed, 30 Oct 2024 13:29:00 -0700 Subject: [PATCH 06/15] Update dsc_lib/src/functions/target_path.rs Co-authored-by: Tess Gauthier --- dsc_lib/src/functions/target_path.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dsc_lib/src/functions/target_path.rs b/dsc_lib/src/functions/target_path.rs index 29ca90d0..87f9fd34 100644 --- a/dsc_lib/src/functions/target_path.rs +++ b/dsc_lib/src/functions/target_path.rs @@ -10,7 +10,7 @@ use tracing::debug; #[derive(Debug, Default)] pub struct TargetPath {} -/// Implements the 'mountedpath' function. +/// Implements the 'TargetPath' function. /// This function returns the value of the mounted path. /// The optional parameter is a path appended to the mounted path. /// Path is not validated as it might be used for creation. From c099c9ea7ea71b8940f8543c0b690810c0e4f09a Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Wed, 30 Oct 2024 13:30:44 -0700 Subject: [PATCH 07/15] fix description of path function --- dsc_lib/src/functions/path.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/dsc_lib/src/functions/path.rs b/dsc_lib/src/functions/path.rs index 5d0c0f83..0ad05255 100644 --- a/dsc_lib/src/functions/path.rs +++ b/dsc_lib/src/functions/path.rs @@ -11,10 +11,9 @@ use tracing::debug; #[derive(Debug, Default)] pub struct Path {} -/// Implements the 'mountedpath' function. -/// This function returns the value of the mounted path. -/// The optional parameter is a path appended to the mounted path. -/// Path is not validated as it might be used for creation. +/// Implements the 'path' function. +/// Accepts a variable number of arguments, each of which is a string. +/// Returns a string that is the concatenation of the arguments, separated by the platform's path separator. impl Function for Path { fn min_args(&self) -> usize { 2 From 405794ab53dd1aa12f6ddedeca945c075d705337 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Wed, 30 Oct 2024 13:34:22 -0700 Subject: [PATCH 08/15] fix clippy --- dsc_lib/src/functions/path.rs | 2 +- dsc_lib/src/functions/target_path.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dsc_lib/src/functions/path.rs b/dsc_lib/src/functions/path.rs index 0ad05255..db439d6c 100644 --- a/dsc_lib/src/functions/path.rs +++ b/dsc_lib/src/functions/path.rs @@ -11,7 +11,7 @@ use tracing::debug; #[derive(Debug, Default)] pub struct Path {} -/// Implements the 'path' function. +/// Implements the `path` function. /// Accepts a variable number of arguments, each of which is a string. /// Returns a string that is the concatenation of the arguments, separated by the platform's path separator. impl Function for Path { diff --git a/dsc_lib/src/functions/target_path.rs b/dsc_lib/src/functions/target_path.rs index 87f9fd34..e828e12e 100644 --- a/dsc_lib/src/functions/target_path.rs +++ b/dsc_lib/src/functions/target_path.rs @@ -10,7 +10,7 @@ use tracing::debug; #[derive(Debug, Default)] pub struct TargetPath {} -/// Implements the 'TargetPath' function. +/// Implements the `targetPath` function. /// This function returns the value of the mounted path. /// The optional parameter is a path appended to the mounted path. /// Path is not validated as it might be used for creation. From 71350a0db67dbbf66385d5b87c04165c2c42b4bd Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Wed, 30 Oct 2024 16:00:21 -0700 Subject: [PATCH 09/15] fix default for targetPath if not specified --- dsc/tests/dsc_functions.tests.ps1 | 20 ++++++++++++++++++++ dsc_lib/src/configure/context.rs | 14 +++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/dsc/tests/dsc_functions.tests.ps1 b/dsc/tests/dsc_functions.tests.ps1 index 7091daeb..1ed8046f 100644 --- a/dsc/tests/dsc_functions.tests.ps1 +++ b/dsc/tests/dsc_functions.tests.ps1 @@ -42,4 +42,24 @@ Describe 'tests for function expressions' { $out = $config_yaml | dsc config --target-path $PSHOME get | ConvertFrom-Json $out.results[0].result.actualState.output | Should -BeExactly $expected } + + It 'default targetPath() is correct for the OS' { + $config_yaml = @' + $schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json + resources: + - name: Echo + type: Microsoft.DSC.Debug/Echo + properties: + output: "[targetPath()]" +'@ + + $expected = if ($IsWindows) { + $env:SYSTEMDRIVE + '\' + } else { + '/' + } + $out = $config_yaml | dsc config get | ConvertFrom-Json + $LASTEXITCODE | Should -Be 0 + $out.results[0].result.actualState.output | Should -BeExactly $expected + } } diff --git a/dsc_lib/src/configure/context.rs b/dsc_lib/src/configure/context.rs index 7263f4fe..9aace0fc 100644 --- a/dsc_lib/src/configure/context.rs +++ b/dsc_lib/src/configure/context.rs @@ -25,7 +25,7 @@ impl Context { Self { execution_type: ExecutionKind::Actual, outputs: HashMap::new(), - target_path: PathBuf::new(), + target_path: get_default_os_target_path(), parameters: HashMap::new(), security_context: match get_security_context() { SecurityContext::Admin => SecurityContextKind::Elevated, @@ -42,3 +42,15 @@ impl Default for Context { Self::new() } } + +#[cfg(target_os = "windows")] +fn get_default_os_target_path() -> PathBuf { + // use SYSTEMDRIVE env var to get the default target path + let system_drive = std::env::var("SYSTEMDRIVE").unwrap_or_else(|_| "C:".to_string()); + PathBuf::from(system_drive).join("\\") +} + +#[cfg(not(target_os = "windows"))] +fn get_default_os_target_path() -> PathBuf { + PathBuf::from("/") +} From fe5f09825fc7955bb27cd01c7db7b9fd79ac845e Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Wed, 30 Oct 2024 16:06:52 -0700 Subject: [PATCH 10/15] fix windows default to not including trailing backslash --- dsc/tests/dsc_functions.tests.ps1 | 2 +- dsc_lib/src/configure/context.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dsc/tests/dsc_functions.tests.ps1 b/dsc/tests/dsc_functions.tests.ps1 index 1ed8046f..3972a348 100644 --- a/dsc/tests/dsc_functions.tests.ps1 +++ b/dsc/tests/dsc_functions.tests.ps1 @@ -54,7 +54,7 @@ Describe 'tests for function expressions' { '@ $expected = if ($IsWindows) { - $env:SYSTEMDRIVE + '\' + $env:SYSTEMDRIVE } else { '/' } diff --git a/dsc_lib/src/configure/context.rs b/dsc_lib/src/configure/context.rs index 9aace0fc..ca30f2ba 100644 --- a/dsc_lib/src/configure/context.rs +++ b/dsc_lib/src/configure/context.rs @@ -47,7 +47,7 @@ impl Default for Context { fn get_default_os_target_path() -> PathBuf { // use SYSTEMDRIVE env var to get the default target path let system_drive = std::env::var("SYSTEMDRIVE").unwrap_or_else(|_| "C:".to_string()); - PathBuf::from(system_drive).join("\\") + PathBuf::from(system_drive) } #[cfg(not(target_os = "windows"))] From 0cd259b1c9cf2071a6579a73fcc3b86a4447c167 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Wed, 30 Oct 2024 17:02:09 -0700 Subject: [PATCH 11/15] fix unit test --- dsc_lib/src/functions/target_path.rs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/dsc_lib/src/functions/target_path.rs b/dsc_lib/src/functions/target_path.rs index e828e12e..997f1677 100644 --- a/dsc_lib/src/functions/target_path.rs +++ b/dsc_lib/src/functions/target_path.rs @@ -38,13 +38,18 @@ impl Function for TargetPath { mod tests { use crate::configure::context::Context; use crate::parser::Statement; - use std::path::PathBuf; + use std::{env, path::PathBuf}; #[test] fn init() { let mut parser = Statement::new().unwrap(); let result = parser.parse_and_execute("[targetPath()]", &Context::new()).unwrap(); - assert_eq!(result, ""); + // on windows, the default is SYSTEMDRIVE env var + #[cfg(target_os = "windows")] + assert_eq!(result, env::var("SYSTEMDRIVE").unwrap()); + // on linux/macOS, the default is / + #[cfg(not(target_os = "windows"))] + assert_eq!(result, "/"); } #[test] From f3ebf4a3754bc84f84715753f3be8cf08213a938 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Sat, 2 Nov 2024 09:35:35 -0700 Subject: [PATCH 12/15] rename targetPath to systemRoot --- dsc/src/args.rs | 4 ++-- dsc/src/main.rs | 6 ++--- dsc/src/subcommand.rs | 2 +- dsc/tests/dsc_functions.tests.ps1 | 6 ++--- dsc_lib/src/configure/context.rs | 8 +++---- dsc_lib/src/configure/mod.rs | 4 ++-- dsc_lib/src/functions/mod.rs | 4 ++-- .../{target_path.rs => system_root.rs} | 22 +++++++++---------- 8 files changed, 27 insertions(+), 29 deletions(-) rename dsc_lib/src/functions/{target_path.rs => system_root.rs} (67%) diff --git a/dsc/src/args.rs b/dsc/src/args.rs index 77edf478..4fa0a997 100644 --- a/dsc/src/args.rs +++ b/dsc/src/args.rs @@ -48,8 +48,8 @@ pub enum SubCommand { parameters: Option, #[clap(short = 'f', long, help = "Parameters to pass to the configuration as a JSON or YAML file", conflicts_with = "parameters")] parameters_file: Option, - #[clap(short = 't', long, help = "Target path to apply configuration, requires resource support.")] - target_path: Option, + #[clap(short = 'r', long, help = "Specify the operating system root path if not targeting the current running OS")] + system_root: Option, // Used to inform when DSC is used as a group resource to modify it's output #[clap(long, hide = true)] as_group: bool, diff --git a/dsc/src/main.rs b/dsc/src/main.rs index 31216d2c..1f578e30 100644 --- a/dsc/src/main.rs +++ b/dsc/src/main.rs @@ -68,11 +68,11 @@ fn main() { let mut cmd = Args::command(); generate(shell, &mut cmd, "dsc", &mut io::stdout()); }, - SubCommand::Config { subcommand, parameters, parameters_file, target_path, as_group, as_include } => { + SubCommand::Config { subcommand, parameters, parameters_file, system_root, as_group, as_include } => { if let Some(file_name) = parameters_file { info!("Reading parameters from file {file_name}"); match std::fs::read_to_string(&file_name) { - Ok(parameters) => subcommand::config(&subcommand, &Some(parameters), &target_path, &input, &as_group, &as_include), + Ok(parameters) => subcommand::config(&subcommand, &Some(parameters), &system_root, &input, &as_group, &as_include), Err(err) => { error!("Error: Failed to read parameters file '{file_name}': {err}"); exit(util::EXIT_INVALID_INPUT); @@ -80,7 +80,7 @@ fn main() { } } else { - subcommand::config(&subcommand, ¶meters, &target_path, &input, &as_group, &as_include); + subcommand::config(&subcommand, ¶meters, &system_root, &input, &as_group, &as_include); } }, SubCommand::Resource { subcommand } => { diff --git a/dsc/src/subcommand.rs b/dsc/src/subcommand.rs index 4c6599a0..dacdd457 100644 --- a/dsc/src/subcommand.rs +++ b/dsc/src/subcommand.rs @@ -279,7 +279,7 @@ pub fn config(subcommand: &ConfigSubCommand, parameters: &Option, mounte exit(EXIT_INVALID_ARGS); } - configurator.set_target_path(path); + configurator.set_system_root(path); } if let Err(err) = configurator.set_context(¶meters) { diff --git a/dsc/tests/dsc_functions.tests.ps1 b/dsc/tests/dsc_functions.tests.ps1 index 3972a348..e6995fb3 100644 --- a/dsc/tests/dsc_functions.tests.ps1 +++ b/dsc/tests/dsc_functions.tests.ps1 @@ -26,7 +26,7 @@ Describe 'tests for function expressions' { } It 'path() works' -TestCases @( - @{ path = "targetPath(), 'a'"; expected = "$PSHOME$([System.IO.Path]::DirectorySeparatorChar)a" } + @{ path = "systemRoot(), 'a'"; expected = "$PSHOME$([System.IO.Path]::DirectorySeparatorChar)a" } @{ path = "'a', 'b', 'c'"; expected = "a$([System.IO.Path]::DirectorySeparatorChar)b$([System.IO.Path]::DirectorySeparatorChar)c" } ) { param($path, $expected) @@ -43,14 +43,14 @@ Describe 'tests for function expressions' { $out.results[0].result.actualState.output | Should -BeExactly $expected } - It 'default targetPath() is correct for the OS' { + It 'default systemRoot() is correct for the OS' { $config_yaml = @' $schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json resources: - name: Echo type: Microsoft.DSC.Debug/Echo properties: - output: "[targetPath()]" + output: "[systemRoot()]" '@ $expected = if ($IsWindows) { diff --git a/dsc_lib/src/configure/context.rs b/dsc_lib/src/configure/context.rs index ca30f2ba..49e90b65 100644 --- a/dsc_lib/src/configure/context.rs +++ b/dsc_lib/src/configure/context.rs @@ -12,7 +12,7 @@ use super::config_doc::{DataType, SecurityContextKind}; pub struct Context { pub execution_type: ExecutionKind, pub outputs: HashMap, // this is used by the `reference()` function to retrieve output - pub target_path: PathBuf, + pub system_root: PathBuf, pub parameters: HashMap, pub security_context: SecurityContextKind, pub variables: HashMap, @@ -25,7 +25,7 @@ impl Context { Self { execution_type: ExecutionKind::Actual, outputs: HashMap::new(), - target_path: get_default_os_target_path(), + system_root: get_default_os_system_root(), parameters: HashMap::new(), security_context: match get_security_context() { SecurityContext::Admin => SecurityContextKind::Elevated, @@ -44,13 +44,13 @@ impl Default for Context { } #[cfg(target_os = "windows")] -fn get_default_os_target_path() -> PathBuf { +fn get_default_os_system_root() -> PathBuf { // use SYSTEMDRIVE env var to get the default target path let system_drive = std::env::var("SYSTEMDRIVE").unwrap_or_else(|_| "C:".to_string()); PathBuf::from(system_drive) } #[cfg(not(target_os = "windows"))] -fn get_default_os_target_path() -> PathBuf { +fn get_default_os_system_root() -> PathBuf { PathBuf::from("/") } diff --git a/dsc_lib/src/configure/mod.rs b/dsc_lib/src/configure/mod.rs index 4aed18c5..a9b9022e 100644 --- a/dsc_lib/src/configure/mod.rs +++ b/dsc_lib/src/configure/mod.rs @@ -485,8 +485,8 @@ impl Configurator { /// # Arguments /// /// * `mounted_path` - The mounted path to set. - pub fn set_target_path(&mut self, target_path: &str) { - self.context.target_path = PathBuf::from(target_path); + pub fn set_system_root(&mut self, system_root: &str) { + self.context.system_root = PathBuf::from(system_root); } /// Set the parameters and variables context for the configuration. diff --git a/dsc_lib/src/functions/mod.rs b/dsc_lib/src/functions/mod.rs index b6559e32..909d73f9 100644 --- a/dsc_lib/src/functions/mod.rs +++ b/dsc_lib/src/functions/mod.rs @@ -23,7 +23,7 @@ pub mod path; pub mod reference; pub mod resource_id; pub mod sub; -pub mod target_path; +pub mod system_root; pub mod variables; /// The kind of argument that a function accepts. @@ -82,7 +82,7 @@ impl FunctionDispatcher { functions.insert("reference".to_string(), Box::new(reference::Reference{})); functions.insert("resourceId".to_string(), Box::new(resource_id::ResourceId{})); functions.insert("sub".to_string(), Box::new(sub::Sub{})); - functions.insert("targetPath".to_string(), Box::new(target_path::TargetPath{})); + functions.insert("systemRoot".to_string(), Box::new(system_root::SystemRoot{})); functions.insert("variables".to_string(), Box::new(variables::Variables{})); Self { functions, diff --git a/dsc_lib/src/functions/target_path.rs b/dsc_lib/src/functions/system_root.rs similarity index 67% rename from dsc_lib/src/functions/target_path.rs rename to dsc_lib/src/functions/system_root.rs index 997f1677..cf885c67 100644 --- a/dsc_lib/src/functions/target_path.rs +++ b/dsc_lib/src/functions/system_root.rs @@ -8,13 +8,11 @@ use serde_json::Value; use tracing::debug; #[derive(Debug, Default)] -pub struct TargetPath {} +pub struct SystemRoot {} -/// Implements the `targetPath` function. -/// This function returns the value of the mounted path. -/// The optional parameter is a path appended to the mounted path. -/// Path is not validated as it might be used for creation. -impl Function for TargetPath { +/// Implements the `systemRoot` function. +/// This function returns the value of the specified system root path. +impl Function for SystemRoot { fn min_args(&self) -> usize { 0 } @@ -30,7 +28,7 @@ impl Function for TargetPath { fn invoke(&self, _args: &[Value], context: &Context) -> Result { debug!("Executing targetPath function"); - Ok(Value::String(context.target_path.to_string_lossy().to_string())) + Ok(Value::String(context.system_root.to_string_lossy().to_string())) } } @@ -38,15 +36,15 @@ impl Function for TargetPath { mod tests { use crate::configure::context::Context; use crate::parser::Statement; - use std::{env, path::PathBuf}; + use std::path::PathBuf; #[test] fn init() { let mut parser = Statement::new().unwrap(); - let result = parser.parse_and_execute("[targetPath()]", &Context::new()).unwrap(); + let result = parser.parse_and_execute("[systemRoot()]", &Context::new()).unwrap(); // on windows, the default is SYSTEMDRIVE env var #[cfg(target_os = "windows")] - assert_eq!(result, env::var("SYSTEMDRIVE").unwrap()); + assert_eq!(result, std::env::var("SYSTEMDRIVE").unwrap()); // on linux/macOS, the default is / #[cfg(not(target_os = "windows"))] assert_eq!(result, "/"); @@ -57,8 +55,8 @@ mod tests { let mut parser = Statement::new().unwrap(); let mut context = Context::new(); let separator = std::path::MAIN_SEPARATOR; - context.target_path = PathBuf::from(format!("{separator}mnt")); - let result = parser.parse_and_execute("[targetPath()]", &context).unwrap(); + context.system_root = PathBuf::from(format!("{separator}mnt")); + let result = parser.parse_and_execute("[systemRoot()]", &context).unwrap(); assert_eq!(result, format!("{separator}mnt")); } From ca2d441c754067e7872ff4bbbc11af6b4f48acbd Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Sun, 3 Nov 2024 10:30:53 -0800 Subject: [PATCH 13/15] fix test --- dsc/tests/dsc_functions.tests.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dsc/tests/dsc_functions.tests.ps1 b/dsc/tests/dsc_functions.tests.ps1 index e6995fb3..7deef03d 100644 --- a/dsc/tests/dsc_functions.tests.ps1 +++ b/dsc/tests/dsc_functions.tests.ps1 @@ -39,7 +39,7 @@ Describe 'tests for function expressions' { properties: output: "[path($path)]" "@ - $out = $config_yaml | dsc config --target-path $PSHOME get | ConvertFrom-Json + $out = $config_yaml | dsc config --system-root $PSHOME get | ConvertFrom-Json $out.results[0].result.actualState.output | Should -BeExactly $expected } From 677b5ec3d49146c7729bb1af48bae0e42be9ceac Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Sun, 3 Nov 2024 13:23:42 -0800 Subject: [PATCH 14/15] fix test --- dsc/tests/dsc_args.tests.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dsc/tests/dsc_args.tests.ps1 b/dsc/tests/dsc_args.tests.ps1 index a63c438b..5b51b527 100644 --- a/dsc/tests/dsc_args.tests.ps1 +++ b/dsc/tests/dsc_args.tests.ps1 @@ -309,8 +309,8 @@ resources: "$TestDrive/tracing.txt" | Should -FileContentMatchExactly 'Can not perform this operation on the adapter' } - It 'Invalid --target-path' { - dsc config --target-path /invalid/path get -p "$PSScriptRoot/../examples/groups.dsc.yaml" 2> $TestDrive/tracing.txt + It 'Invalid --system-root' { + dsc config --system-root /invalid/path get -p "$PSScriptRoot/../examples/groups.dsc.yaml" 2> $TestDrive/tracing.txt $LASTEXITCODE | Should -Be 1 "$TestDrive/tracing.txt" | Should -FileContentMatchExactly "Target path '/invalid/path' does not exist" } From 5e1ffb322cad4be2357feac3a822d054623917a3 Mon Sep 17 00:00:00 2001 From: Steve Lee Date: Fri, 15 Nov 2024 11:58:11 -0800 Subject: [PATCH 15/15] Update dsc_lib/src/configure/mod.rs Co-authored-by: Tess Gauthier --- dsc_lib/src/configure/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dsc_lib/src/configure/mod.rs b/dsc_lib/src/configure/mod.rs index a9b9022e..41bfb6f8 100644 --- a/dsc_lib/src/configure/mod.rs +++ b/dsc_lib/src/configure/mod.rs @@ -484,7 +484,7 @@ impl Configurator { /// /// # Arguments /// - /// * `mounted_path` - The mounted path to set. + /// * `system_root` - The system root to set. pub fn set_system_root(&mut self, system_root: &str) { self.context.system_root = PathBuf::from(system_root); }