Skip to content

Commit 15e04f7

Browse files
author
Steve Lee (POWERSHELL HE/HIM) (from Dev Box)
committed
Add --mounted-path parameter to dsc
1 parent c209290 commit 15e04f7

File tree

8 files changed

+116
-4
lines changed

8 files changed

+116
-4
lines changed

dsc/src/args.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ pub enum SubCommand {
4848
parameters: Option<String>,
4949
#[clap(short = 'f', long, help = "Parameters to pass to the configuration as a JSON or YAML file", conflicts_with = "parameters")]
5050
parameters_file: Option<String>,
51+
#[clap(short = 'm', long, help = "Mounted path to use for supported resources")]
52+
mounted_path: Option<String>,
5153
// Used to inform when DSC is used as a group resource to modify it's output
5254
#[clap(long, hide = true)]
5355
as_group: bool,

dsc/src/main.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,19 +68,19 @@ fn main() {
6868
let mut cmd = Args::command();
6969
generate(shell, &mut cmd, "dsc", &mut io::stdout());
7070
},
71-
SubCommand::Config { subcommand, parameters, parameters_file, as_group, as_include } => {
71+
SubCommand::Config { subcommand, parameters, parameters_file, mounted_path, as_group, as_include } => {
7272
if let Some(file_name) = parameters_file {
7373
info!("Reading parameters from file {file_name}");
7474
match std::fs::read_to_string(&file_name) {
75-
Ok(parameters) => subcommand::config(&subcommand, &Some(parameters), &input, &as_group, &as_include),
75+
Ok(parameters) => subcommand::config(&subcommand, &Some(parameters), &mounted_path, &input, &as_group, &as_include),
7676
Err(err) => {
7777
error!("Error: Failed to read parameters file '{file_name}': {err}");
7878
exit(util::EXIT_INVALID_INPUT);
7979
}
8080
}
8181
}
8282
else {
83-
subcommand::config(&subcommand, &parameters, &input, &as_group, &as_include);
83+
subcommand::config(&subcommand, &parameters, &mounted_path, &input, &as_group, &as_include);
8484
}
8585
},
8686
SubCommand::Resource { subcommand } => {

dsc/src/subcommand.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ 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, as_include: &bool) {
189+
pub fn config(subcommand: &ConfigSubCommand, parameters: &Option<String>, mounted_path: &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, .. } |
@@ -270,6 +270,10 @@ pub fn config(subcommand: &ConfigSubCommand, parameters: &Option<String>, stdin:
270270
}
271271
};
272272

273+
if let Some(path) = mounted_path {
274+
configurator.set_mounted_path(path);
275+
}
276+
273277
if let Err(err) = configurator.set_context(&parameters) {
274278
error!("Error: Parameter input failure: {err}");
275279
exit(EXIT_INVALID_INPUT);

dsc/tests/dsc_functions.tests.ps1

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,31 @@ Describe 'tests for function expressions' {
2424
$out = $config_yaml | dsc config get | ConvertFrom-Json
2525
$out.results[0].result.actualState.output | Should -Be $expected
2626
}
27+
28+
It 'mountedpath(<path>) works' -TestCases @(
29+
@{ path = '' }
30+
@{ path = "hello$([System.IO.Path]::DirectorySeparatorChar)world" }
31+
) {
32+
param($path)
33+
34+
$testPath = if ($path.Length -gt 0) {
35+
$expected = "$PSHOME$([System.IO.Path]::DirectorySeparatorChar)$path"
36+
"'$($path.replace('\', '\\'))'"
37+
}
38+
else {
39+
$expected = $PSHOME
40+
$path
41+
}
42+
43+
$config_yaml = @"
44+
`$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json
45+
resources:
46+
- name: Echo
47+
type: Microsoft.DSC.Debug/Echo
48+
properties:
49+
output: "[mountedpath($testPath)]"
50+
"@
51+
$out = $config_yaml | dsc config --mounted-path $PSHOME get | ConvertFrom-Json
52+
$out.results[0].result.actualState.output | Should -BeExactly $expected
53+
}
2754
}

dsc_lib/src/configure/context.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use super::config_doc::{DataType, SecurityContextKind};
1212
pub struct Context {
1313
pub execution_type: ExecutionKind,
1414
pub outputs: HashMap<String, Value>, // this is used by the `reference()` function to retrieve output
15+
pub mounted_path: String,
1516
pub parameters: HashMap<String, (Value, DataType)>,
1617
pub security_context: SecurityContextKind,
1718
pub variables: HashMap<String, Value>,
@@ -24,6 +25,7 @@ impl Context {
2425
Self {
2526
execution_type: ExecutionKind::Actual,
2627
outputs: HashMap::new(),
28+
mounted_path: String::new(),
2729
parameters: HashMap::new(),
2830
security_context: match get_security_context() {
2931
SecurityContext::Admin => SecurityContextKind::Elevated,

dsc_lib/src/configure/mod.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,15 @@ impl Configurator {
479479
Ok(result)
480480
}
481481

482+
/// Set the mounted path for the configuration.
483+
///
484+
/// # Arguments
485+
///
486+
/// * `mounted_path` - The mounted path to set.
487+
pub fn set_mounted_path(&mut self, mounted_path: &str) {
488+
self.context.mounted_path = mounted_path.to_string();
489+
}
490+
482491
/// Set the parameters and variables context for the configuration.
483492
///
484493
/// # Arguments

dsc_lib/src/functions/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ pub mod int;
1717
pub mod max;
1818
pub mod min;
1919
pub mod mod_function;
20+
pub mod mounted_path;
2021
pub mod mul;
2122
pub mod parameters;
2223
pub mod reference;
@@ -74,6 +75,7 @@ impl FunctionDispatcher {
7475
functions.insert("max".to_string(), Box::new(max::Max{}));
7576
functions.insert("min".to_string(), Box::new(min::Min{}));
7677
functions.insert("mod".to_string(), Box::new(mod_function::Mod{}));
78+
functions.insert("mountedpath".to_string(), Box::new(mounted_path::MountedPath{}));
7779
functions.insert("mul".to_string(), Box::new(mul::Mul{}));
7880
functions.insert("parameters".to_string(), Box::new(parameters::Parameters{}));
7981
functions.insert("reference".to_string(), Box::new(reference::Reference{}));

dsc_lib/src/functions/mounted_path.rs

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
use crate::DscError;
5+
use crate::configure::context::Context;
6+
use crate::functions::{AcceptedArgKind, Function};
7+
use serde_json::Value;
8+
use tracing::debug;
9+
10+
#[derive(Debug, Default)]
11+
pub struct MountedPath {}
12+
13+
/// Implements the 'mountedpath' function.
14+
/// This function returns the value of the mounted path.
15+
/// The optional parameter is a path appended to the mounted path.
16+
/// Path is not validated as it might be used for creation.
17+
impl Function for MountedPath {
18+
fn min_args(&self) -> usize {
19+
0
20+
}
21+
22+
fn max_args(&self) -> usize {
23+
1
24+
}
25+
26+
fn accepted_arg_types(&self) -> Vec<AcceptedArgKind> {
27+
vec![AcceptedArgKind::String]
28+
}
29+
30+
fn invoke(&self, args: &[Value], context: &Context) -> Result<Value, DscError> {
31+
debug!("Executing mountedpath function with args: {:?}", args);
32+
33+
if args.len() == 1 {
34+
let path = args[0].as_str().ok_or(DscError::Parser("Invalid argument".to_string()))?;
35+
Ok(Value::String(format!("{}{}{path}", context.mounted_path, std::path::MAIN_SEPARATOR)))
36+
} else {
37+
Ok(Value::String(context.mounted_path.clone()))
38+
}
39+
}
40+
}
41+
42+
#[cfg(test)]
43+
mod tests {
44+
use crate::configure::context::Context;
45+
use crate::parser::Statement;
46+
47+
#[test]
48+
fn no_arg() {
49+
let mut parser = Statement::new().unwrap();
50+
let mut context = Context::new();
51+
let separator = std::path::MAIN_SEPARATOR;
52+
context.mounted_path = format!("{separator}mnt");
53+
let result = parser.parse_and_execute("[mountedpath()]", &context).unwrap();
54+
assert_eq!(result, format!("{separator}mnt"));
55+
}
56+
57+
#[test]
58+
fn with_arg() {
59+
let mut parser = Statement::new().unwrap();
60+
let mut context = Context::new();
61+
let separator = std::path::MAIN_SEPARATOR;
62+
context.mounted_path = format!("{separator}mnt");
63+
let result = parser.parse_and_execute("[mountedpath('foo')]", &context).unwrap();
64+
assert_eq!(result, format!("{separator}mnt{separator}foo"));
65+
}
66+
}

0 commit comments

Comments
 (0)