Skip to content

Commit ab7d491

Browse files
committed
change to --target-path and targetPath(). add path() function
1 parent 15e04f7 commit ab7d491

File tree

11 files changed

+130
-55
lines changed

11 files changed

+130
-55
lines changed

.vscode/settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"rust-analyzer.linkedProjects": [
33
"./dsc/Cargo.toml",
44
"./dsc_lib/Cargo.toml",
5-
"./echo/Cargo.toml",
5+
"./dscecho/Cargo.toml",
66
"./osinfo/Cargo.toml",
77
"./registry/Cargo.toml",
88
"./runcommandonset/Cargo.toml",

dsc/src/args.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +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>,
51+
#[clap(short = 't', long, help = "Target path to apply configuration, requires resource support.")]
52+
target_path: Option<String>,
5353
// Used to inform when DSC is used as a group resource to modify it's output
5454
#[clap(long, hide = true)]
5555
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, mounted_path, as_group, as_include } => {
71+
SubCommand::Config { subcommand, parameters, parameters_file, target_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), &mounted_path, &input, &as_group, &as_include),
75+
Ok(parameters) => subcommand::config(&subcommand, &Some(parameters), &target_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, &mounted_path, &input, &as_group, &as_include);
83+
subcommand::config(&subcommand, &parameters, &target_path, &input, &as_group, &as_include);
8484
}
8585
},
8686
SubCommand::Resource { subcommand } => {

dsc/src/subcommand.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::args::{ConfigSubCommand, DscType, OutputFormat, ResourceSubCommand};
55
use crate::resolve::{get_contents, Include};
66
use crate::resource_command::{get_resource, self};
77
use crate::tablewriter::Table;
8-
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};
8+
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};
99
use dsc_lib::configure::{Configurator, config_doc::{Configuration, ExecutionKind}, config_result::ResourceGetResult};
1010
use dsc_lib::dscerror::DscError;
1111
use dsc_lib::dscresources::invoke_result::ResolveResult;
@@ -15,9 +15,12 @@ use dsc_lib::{
1515
dscresources::dscresource::{Capability, ImplementedAs, Invoke},
1616
dscresources::resource_manifest::{import_manifest, ResourceManifest},
1717
};
18-
use std::collections::HashMap;
19-
use std::io::{self, IsTerminal};
20-
use std::process::exit;
18+
use std::{
19+
collections::HashMap,
20+
io::{self, IsTerminal},
21+
path::Path,
22+
process::exit
23+
};
2124
use tracing::{debug, error, trace};
2225

2326
pub fn config_get(configurator: &mut Configurator, format: &Option<OutputFormat>, as_group: &bool)
@@ -271,7 +274,12 @@ pub fn config(subcommand: &ConfigSubCommand, parameters: &Option<String>, mounte
271274
};
272275

273276
if let Some(path) = mounted_path {
274-
configurator.set_mounted_path(path);
277+
if !Path::new(&path).exists() {
278+
error!("Error: Target path '{path}' does not exist");
279+
exit(EXIT_INVALID_ARGS);
280+
}
281+
282+
configurator.set_target_path(path);
275283
}
276284

277285
if let Err(err) = configurator.set_context(&parameters) {

dsc/tests/dsc_args.tests.ps1

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,4 +308,10 @@ resources:
308308
$LASTEXITCODE | Should -Be 2
309309
"$TestDrive/tracing.txt" | Should -FileContentMatchExactly 'Can not perform this operation on the adapter'
310310
}
311+
312+
It 'Invalid --target-path' {
313+
dsc config --target-path /invalid/path get -p "$PSScriptRoot/../examples/groups.dsc.yaml" 2> $TestDrive/tracing.txt
314+
$LASTEXITCODE | Should -Be 1
315+
"$TestDrive/tracing.txt" | Should -FileContentMatchExactly "Target path '/invalid/path' does not exist"
316+
}
311317
}

dsc/tests/dsc_functions.tests.ps1

Lines changed: 11 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
# Licensed under the MIT License.
33

44
Describe 'tests for function expressions' {
5+
BeforeAll {
6+
$global:sep = [System.IO.Path]::DirectorySeparatorChar
7+
}
8+
9+
510
It 'function works: <text>' -TestCases @(
611
@{ text = "[concat('a', 'b')]"; expected = 'ab' }
712
@{ text = "[concat('a', 'b', 'c')]"; expected = 'abc' }
@@ -25,30 +30,21 @@ Describe 'tests for function expressions' {
2530
$out.results[0].result.actualState.output | Should -Be $expected
2631
}
2732

28-
It 'mountedpath(<path>) works' -TestCases @(
29-
@{ path = '' }
30-
@{ path = "hello$([System.IO.Path]::DirectorySeparatorChar)world" }
33+
It 'path(<path>) works' -TestCases @(
34+
@{ path = "targetPath(), 'a'"; expected = "$PSHOME${sep}a" }
35+
@{ path = "'a', 'b', 'c'"; expected = "a${sep}b${sep}c" }
3136
) {
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-
}
37+
param($path, $expected)
4238

4339
$config_yaml = @"
4440
`$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json
4541
resources:
4642
- name: Echo
4743
type: Microsoft.DSC.Debug/Echo
4844
properties:
49-
output: "[mountedpath($testPath)]"
45+
output: "[path($path)]"
5046
"@
51-
$out = $config_yaml | dsc config --mounted-path $PSHOME get | ConvertFrom-Json
47+
$out = $config_yaml | dsc config --target-path $PSHOME get | ConvertFrom-Json
5248
$out.results[0].result.actualState.output | Should -BeExactly $expected
5349
}
5450
}

dsc_lib/src/configure/context.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@ use chrono::{DateTime, Local};
55
use crate::configure::config_doc::ExecutionKind;
66
use security_context_lib::{get_security_context, SecurityContext};
77
use serde_json::Value;
8-
use std::collections::HashMap;
8+
use std::{collections::HashMap, path::PathBuf};
99

1010
use super::config_doc::{DataType, SecurityContextKind};
1111

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,
15+
pub target_path: PathBuf,
1616
pub parameters: HashMap<String, (Value, DataType)>,
1717
pub security_context: SecurityContextKind,
1818
pub variables: HashMap<String, Value>,
@@ -25,7 +25,7 @@ impl Context {
2525
Self {
2626
execution_type: ExecutionKind::Actual,
2727
outputs: HashMap::new(),
28-
mounted_path: String::new(),
28+
target_path: PathBuf::new(),
2929
parameters: HashMap::new(),
3030
security_context: match get_security_context() {
3131
SecurityContext::Admin => SecurityContextKind::Elevated,

dsc_lib/src/configure/mod.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use self::contraints::{check_length, check_number_limits, check_allowed_values};
2020
use indicatif::ProgressStyle;
2121
use security_context_lib::{SecurityContext, get_security_context};
2222
use serde_json::{Map, Value};
23+
use std::path::PathBuf;
2324
use std::{collections::HashMap, mem};
2425
use tracing::{debug, info, trace, warn_span, Span};
2526
use tracing_indicatif::span_ext::IndicatifSpanExt;
@@ -480,12 +481,12 @@ impl Configurator {
480481
}
481482

482483
/// Set the mounted path for the configuration.
483-
///
484+
///
484485
/// # Arguments
485-
///
486+
///
486487
/// * `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();
488+
pub fn set_target_path(&mut self, target_path: &str) {
489+
self.context.target_path = PathBuf::from(target_path);
489490
}
490491

491492
/// Set the parameters and variables context for the configuration.

dsc_lib/src/functions/mod.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,13 @@ pub mod int;
1717
pub mod max;
1818
pub mod min;
1919
pub mod mod_function;
20-
pub mod mounted_path;
2120
pub mod mul;
2221
pub mod parameters;
22+
pub mod path;
2323
pub mod reference;
2424
pub mod resource_id;
2525
pub mod sub;
26+
pub mod target_path;
2627
pub mod variables;
2728

2829
/// The kind of argument that a function accepts.
@@ -75,12 +76,13 @@ impl FunctionDispatcher {
7576
functions.insert("max".to_string(), Box::new(max::Max{}));
7677
functions.insert("min".to_string(), Box::new(min::Min{}));
7778
functions.insert("mod".to_string(), Box::new(mod_function::Mod{}));
78-
functions.insert("mountedpath".to_string(), Box::new(mounted_path::MountedPath{}));
7979
functions.insert("mul".to_string(), Box::new(mul::Mul{}));
8080
functions.insert("parameters".to_string(), Box::new(parameters::Parameters{}));
81+
functions.insert("path".to_string(), Box::new(path::Path{}));
8182
functions.insert("reference".to_string(), Box::new(reference::Reference{}));
8283
functions.insert("resourceId".to_string(), Box::new(resource_id::ResourceId{}));
8384
functions.insert("sub".to_string(), Box::new(sub::Sub{}));
85+
functions.insert("targetPath".to_string(), Box::new(target_path::TargetPath{}));
8486
functions.insert("variables".to_string(), Box::new(variables::Variables{}));
8587
Self {
8688
functions,

dsc_lib/src/functions/path.rs

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

0 commit comments

Comments
 (0)