Skip to content

Commit 6a4b087

Browse files
committed
Fix allow copy resources to have same name
1 parent 4886bd0 commit 6a4b087

File tree

3 files changed

+167
-130
lines changed

3 files changed

+167
-130
lines changed

dsc/tests/dsc_copy.tests.ps1

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,4 +334,48 @@ resources:
334334
$out.results[1].name | Should -Be 'Server-1'
335335
$out.results[1].result.actualState.output | Should -Be 'web-2'
336336
}
337+
338+
It 'Symbolic name loop works' {
339+
$configYaml = @'
340+
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
341+
languageVersion: 2.2-experimental
342+
contentVersion: 1.0.0.0
343+
parameters:
344+
basePath:
345+
type: string
346+
defaultValue: DSCBicepTests
347+
variables:
348+
items:
349+
- A
350+
- B
351+
- C
352+
extensions:
353+
dsc:
354+
name: DesiredStateConfiguration
355+
version: 0.1.0
356+
resources:
357+
simple_loop:
358+
copy:
359+
name: simple_loop
360+
count: '[length(variables(''items''))]'
361+
extension: dsc
362+
type: Microsoft.DSC.Debug/Echo
363+
properties:
364+
output: '[format(''{0}\loops_simple_{1}'', parameters(''basePath''), variables(''items'')[copyIndex()])]'
365+
outputs:
366+
simpleLoopCount:
367+
type: int
368+
value: '[length(variables(''items''))]'
369+
'@
370+
$out = dsc -l trace config get -i $configYaml 2>$testdrive/error.log | ConvertFrom-Json
371+
$LASTEXITCODE | Should -Be 0 -Because (Get-Content $testdrive/error.log -Raw | Out-String)
372+
$out.results.Count | Should -Be 3
373+
$out.results[0].name | Should -Be 'simple_loop'
374+
$out.results[0].result.actualState.output | Should -Be 'DSCBicepTests\loops_simple_A' -Because ($out | ConvertTo-Json -Depth 10)
375+
$out.results[1].name | Should -Be 'simple_loop'
376+
$out.results[1].result.actualState.output | Should -Be 'DSCBicepTests\loops_simple_B'
377+
$out.results[2].name | Should -Be 'simple_loop'
378+
$out.results[2].result.actualState.output | Should -Be 'DSCBicepTests\loops_simple_C'
379+
$out.outputs.simpleLoopCount | Should -Be 3
380+
}
337381
}

lib/dsc-lib/src/configure/depends_on.rs

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

44
use crate::configure::config_doc::Resource;
5-
use crate::configure::Configuration;
5+
use crate::configure::{Configuration, IntOrExpression, ProcessMode, invoke_property_expressions};
66
use crate::DscError;
77
use crate::parser::Statement;
88

99
use rust_i18n::t;
10+
use serde_json::Value;
1011
use super::context::Context;
1112
use tracing::debug;
1213

@@ -23,7 +24,7 @@ use tracing::debug;
2324
/// # Errors
2425
///
2526
/// * `DscError::Validation` - The configuration is invalid
26-
pub fn get_resource_invocation_order(config: &Configuration, parser: &mut Statement, context: &Context) -> Result<Vec<Resource>, DscError> {
27+
pub fn get_resource_invocation_order(config: &Configuration, parser: &mut Statement, context: &mut Context) -> Result<Vec<Resource>, DscError> {
2728
debug!("Getting resource invocation order");
2829
let mut order: Vec<Resource> = Vec::new();
2930
for resource in &config.resources {
@@ -54,7 +55,7 @@ pub fn get_resource_invocation_order(config: &Configuration, parser: &mut Statem
5455
continue;
5556
}
5657
// add the dependency to the order
57-
order.push(dependency_resource.clone());
58+
unroll_and_push(&mut order, dependency_resource, parser, context)?;
5859
dependency_already_in_order = false;
5960
}
6061
}
@@ -84,13 +85,52 @@ pub fn get_resource_invocation_order(config: &Configuration, parser: &mut Statem
8485
continue;
8586
}
8687

87-
order.push(resource.clone());
88+
unroll_and_push(&mut order, resource, parser, context)?;
8889
}
8990

9091
debug!("{}: {order:?}", t!("configure.dependsOn.invocationOrder"));
9192
Ok(order)
9293
}
9394

95+
fn unroll_and_push(order: &mut Vec<Resource>, resource: &Resource, parser: &mut Statement, context: &mut Context) -> Result<(), DscError> {
96+
// if the resource contains `Copy`, unroll it
97+
if let Some(copy) = &resource.copy {
98+
debug!("{}", t!("configure.mod.unrollingCopy", name = &copy.name, count = copy.count));
99+
context.process_mode = ProcessMode::Copy;
100+
context.copy_current_loop_name.clone_from(&copy.name);
101+
let mut copy_resources = Vec::<Resource>::new();
102+
let count: i64 = match &copy.count {
103+
IntOrExpression::Int(i) => *i,
104+
IntOrExpression::Expression(e) => {
105+
let Value::Number(n) = parser.parse_and_execute(e, context)? else {
106+
return Err(DscError::Parser(t!("configure.mod.copyCountResultNotInteger", expression = e).to_string()))
107+
};
108+
n.as_i64().ok_or_else(|| DscError::Parser(t!("configure.mod.copyCountResultNotInteger", expression = e).to_string()))?
109+
},
110+
};
111+
for i in 0..count {
112+
context.copy.insert(copy.name.clone(), i);
113+
let mut new_resource = resource.clone();
114+
let Value::String(new_name) = parser.parse_and_execute(&resource.name, context)? else {
115+
return Err(DscError::Parser(t!("configure.mod.copyNameResultNotString").to_string()))
116+
};
117+
new_resource.name = new_name.to_string();
118+
119+
if let Some(properties) = &resource.properties {
120+
new_resource.properties = invoke_property_expressions(parser, context, Some(properties))?;
121+
}
122+
123+
new_resource.copy = None;
124+
copy_resources.push(new_resource);
125+
}
126+
context.process_mode = ProcessMode::Normal;
127+
order.extend(copy_resources.clone());
128+
} else {
129+
order.push(resource.clone());
130+
}
131+
Ok(())
132+
}
133+
94134
fn get_type_and_name(statement: &str) -> Result<(&str, String), DscError> {
95135
let parts: Vec<&str> = statement.split(':').collect();
96136
if parts.len() != 2 {
@@ -122,7 +162,8 @@ mod tests {
122162

123163
let config: Configuration = serde_yaml::from_str(config_yaml).unwrap();
124164
let mut parser = parser::Statement::new().unwrap();
125-
let order = get_resource_invocation_order(&config, &mut parser, &Context::new()).unwrap();
165+
let mut context = Context::new();
166+
let order = get_resource_invocation_order(&config, &mut parser, &mut context).unwrap();
126167
assert_eq!(order[0].name, "First");
127168
assert_eq!(order[1].name, "Second");
128169
}
@@ -144,7 +185,8 @@ mod tests {
144185

145186
let config: Configuration = serde_yaml::from_str(config_yaml).unwrap();
146187
let mut parser = parser::Statement::new().unwrap();
147-
let order = get_resource_invocation_order(&config, &mut parser, &Context::new());
188+
let mut context = Context::new();
189+
let order = get_resource_invocation_order(&config, &mut parser, &mut context);
148190
assert!(order.is_err());
149191
}
150192

@@ -161,7 +203,8 @@ mod tests {
161203

162204
let config: Configuration = serde_yaml::from_str(config_yaml).unwrap();
163205
let mut parser = parser::Statement::new().unwrap();
164-
let order = get_resource_invocation_order(&config, &mut parser, &Context::new());
206+
let mut context = Context::new();
207+
let order = get_resource_invocation_order(&config, &mut parser, &mut context);
165208
assert!(order.is_err());
166209
}
167210

@@ -184,7 +227,8 @@ mod tests {
184227

185228
let config: Configuration = serde_yaml::from_str(config_yaml).unwrap();
186229
let mut parser = parser::Statement::new().unwrap();
187-
let order = get_resource_invocation_order(&config, &mut parser, &Context::new()).unwrap();
230+
let mut context = Context::new();
231+
let order = get_resource_invocation_order(&config, &mut parser, &mut context).unwrap();
188232
assert_eq!(order[0].name, "First");
189233
assert_eq!(order[1].name, "Second");
190234
assert_eq!(order[2].name, "Third");
@@ -207,7 +251,8 @@ mod tests {
207251

208252
let config: Configuration = serde_yaml::from_str(config_yaml).unwrap();
209253
let mut parser = parser::Statement::new().unwrap();
210-
let order = get_resource_invocation_order(&config, &mut parser, &Context::new());
254+
let mut context = Context::new();
255+
let order = get_resource_invocation_order(&config, &mut parser, &mut context);
211256
assert!(order.is_err());
212257
}
213258

@@ -229,7 +274,8 @@ mod tests {
229274

230275
let config: Configuration = serde_yaml::from_str(config_yaml).unwrap();
231276
let mut parser = parser::Statement::new().unwrap();
232-
let order = get_resource_invocation_order(&config, &mut parser, &Context::new()).unwrap();
277+
let mut context = Context::new();
278+
let order = get_resource_invocation_order(&config, &mut parser, &mut context).unwrap();
233279
assert_eq!(order[0].name, "First");
234280
assert_eq!(order[1].name, "Second");
235281
assert_eq!(order[2].name, "Third");
@@ -257,7 +303,8 @@ mod tests {
257303

258304
let config: Configuration = serde_yaml::from_str(config_yaml).unwrap();
259305
let mut parser = parser::Statement::new().unwrap();
260-
let order = get_resource_invocation_order(&config, &mut parser, &Context::new());
306+
let mut context = Context::new();
307+
let order = get_resource_invocation_order(&config, &mut parser, &mut context);
261308
assert!(order.is_err());
262309
}
263310

@@ -285,7 +332,8 @@ mod tests {
285332

286333
let config: Configuration = serde_yaml::from_str(config_yaml).unwrap();
287334
let mut parser = parser::Statement::new().unwrap();
288-
let order = get_resource_invocation_order(&config, &mut parser, &Context::new()).unwrap();
335+
let mut context = Context::new();
336+
let order = get_resource_invocation_order(&config, &mut parser, &mut context).unwrap();
289337
assert_eq!(order[0].name, "First");
290338
assert_eq!(order[1].name, "Second");
291339
assert_eq!(order[2].name, "Third");

0 commit comments

Comments
 (0)