Skip to content

Commit 6581288

Browse files
authored
Merge pull request #291 from SteveL-MSFT/parameters
Add parameters support
2 parents 52329a5 + 7b7d580 commit 6581288

23 files changed

+696
-63
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
parameters:
2+
osFamily: macOS
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
2+
parameters:
3+
osFamily:
4+
type: string
5+
defaultValue: Windows
6+
allowedValues:
7+
- Windows
8+
- Linux
9+
- macOS
10+
resources:
11+
- name: os
12+
type: Microsoft/OSInfo
13+
properties:
14+
family: "[parameters('osFamily')]"

dsc/src/args.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ pub enum SubCommand {
4040
Config {
4141
#[clap(subcommand)]
4242
subcommand: ConfigSubCommand,
43+
#[clap(short, long, help = "Parameters to pass to the configuration as JSON or YAML", conflicts_with = "parameters_file")]
44+
parameters: Option<String>,
45+
#[clap(short = 'f', long, help = "Parameters to pass to the configuration as a JSON or YAML file", conflicts_with = "parameters")]
46+
parameters_file: Option<String>,
4347
},
4448
#[clap(name = "resource", about = "Invoke a specific DSC resource")]
4549
Resource {

dsc/src/main.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,20 @@ fn main() {
8181
let mut cmd = Args::command();
8282
generate(shell, &mut cmd, "dsc", &mut io::stdout());
8383
},
84-
SubCommand::Config { subcommand } => {
85-
subcommand::config(&subcommand, &args.format, &input);
84+
SubCommand::Config { subcommand, parameters, parameters_file } => {
85+
if let Some(file_name) = parameters_file {
86+
info!("Reading parameters from file {}", file_name);
87+
match std::fs::read_to_string(file_name) {
88+
Ok(parameters) => subcommand::config(&subcommand, &Some(parameters), &args.format, &input),
89+
Err(err) => {
90+
error!("Error: Failed to read parameters file: {err}");
91+
exit(util::EXIT_INVALID_INPUT);
92+
}
93+
}
94+
}
95+
else {
96+
subcommand::config(&subcommand, &parameters, &args.format, &input);
97+
}
8698
},
8799
SubCommand::Resource { subcommand } => {
88100
subcommand::resource(&subcommand, &args.format, &input);

dsc/src/subcommand.rs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ pub fn config_export(configurator: &mut Configurator, format: &Option<OutputForm
116116
}
117117
}
118118

119-
pub fn config(subcommand: &ConfigSubCommand, format: &Option<OutputFormat>, stdin: &Option<String>) {
119+
pub fn config(subcommand: &ConfigSubCommand, parameters: &Option<String>, format: &Option<OutputFormat>, stdin: &Option<String>) {
120120
let Some(stdin) = stdin else {
121121
error!("Configuration must be piped to STDIN");
122122
exit(EXIT_INVALID_ARGS);
@@ -152,6 +152,37 @@ pub fn config(subcommand: &ConfigSubCommand, format: &Option<OutputFormat>, stdi
152152
}
153153
};
154154

155+
let parameters: Option<serde_json::Value> = match parameters {
156+
None => None,
157+
Some(parameters) => {
158+
match serde_json::from_str(parameters) {
159+
Ok(json) => Some(json),
160+
Err(_) => {
161+
match serde_yaml::from_str::<Value>(parameters) {
162+
Ok(yaml) => {
163+
match serde_json::to_value(yaml) {
164+
Ok(json) => Some(json),
165+
Err(err) => {
166+
error!("Error: Failed to convert YAML to JSON: {err}");
167+
exit(EXIT_DSC_ERROR);
168+
}
169+
}
170+
},
171+
Err(err) => {
172+
error!("Error: Parameters are not valid JSON or YAML: {err}");
173+
exit(EXIT_INVALID_INPUT);
174+
}
175+
}
176+
}
177+
}
178+
}
179+
};
180+
181+
if let Err(err) = configurator.set_parameters(&parameters) {
182+
error!("Error: Paramter input failure: {err}");
183+
exit(EXIT_INVALID_INPUT);
184+
}
185+
155186
match subcommand {
156187
ConfigSubCommand::Get => {
157188
config_get(&mut configurator, format);
@@ -383,7 +414,7 @@ pub fn resource(subcommand: &ResourceSubCommand, format: &Option<OutputFormat>,
383414
ResourceSubCommand::Get { resource, input, all } => {
384415
dsc.discover_resources(&[resource.to_lowercase().to_string()]);
385416
if *all { resource_command::get_all(&dsc, resource, input, stdin, format); }
386-
else {
417+
else {
387418
resource_command::get(&dsc, resource, input, stdin, format);
388419
};
389420
},

dsc/tests/dsc_parameters.tests.ps1

Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# Licensed under the MIT License.
3+
4+
Describe 'Parameters tests' {
5+
It 'Input can be provided as <inputType>' -TestCases @(
6+
@{ inputType = 'string' }
7+
@{ inputType = 'file' }
8+
) {
9+
param($inputType)
10+
11+
$config_yaml = @"
12+
`$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/10/config/document.json
13+
parameters:
14+
param1:
15+
type: string
16+
resources:
17+
- name: Echo
18+
type: Test/Echo
19+
properties:
20+
text: '[parameters(''param1'')]'
21+
"@
22+
$params_json = @{ parameters = @{ param1 = 'hello' }} | ConvertTo-Json
23+
24+
if ($inputType -eq 'file') {
25+
$file_path = "$TestDrive/test.parameters.json"
26+
Set-Content -Path $file_path -Value $params_json
27+
$out = $config_yaml | dsc config -f $file_path get | ConvertFrom-Json
28+
}
29+
else {
30+
$out = $config_yaml | dsc config -p $params_json get | ConvertFrom-Json
31+
}
32+
33+
$LASTEXITCODE | Should -Be 0
34+
$out.results[0].result.actualState.text | Should -BeExactly '"hello"'
35+
}
36+
37+
It 'Input is <type>' -TestCases @(
38+
@{ type = 'string'; value = 'hello'; expected = '"hello"' }
39+
@{ type = 'int'; value = 42; expected = 42 }
40+
@{ type = 'bool'; value = $true; expected = $true }
41+
@{ type = 'array'; value = @('hello', 'world'); expected = '["hello","world"]' }
42+
) {
43+
param($type, $value, $expected)
44+
45+
$config_yaml = @"
46+
`$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/10/config/document.json
47+
parameters:
48+
param1:
49+
type: $type
50+
resources:
51+
- name: Echo
52+
type: Test/Echo
53+
properties:
54+
text: '[parameters(''param1'')]'
55+
"@
56+
$params_json = @{ parameters = @{ param1 = $value }} | ConvertTo-Json
57+
58+
$out = $config_yaml | dsc config -p $params_json get | ConvertFrom-Json
59+
$LASTEXITCODE | Should -Be 0
60+
$out.results[0].result.actualState.text | Should -BeExactly $expected
61+
}
62+
63+
It 'Input is incorrect type <type>' -TestCases @(
64+
@{ type = 'string'; value = 42 }
65+
@{ type = 'int'; value = 'hello' }
66+
@{ type = 'bool'; value = 'hello' }
67+
@{ type = 'array'; value = 'hello' }
68+
) {
69+
param($type, $value)
70+
71+
$config_yaml = @"
72+
`$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/10/config/document.json
73+
parameters:
74+
param1:
75+
type: $type
76+
resources:
77+
- name: Echo
78+
type: Test/Echo
79+
properties:
80+
text: '[parameters(''param1'')]'
81+
"@
82+
$params_json = @{ parameters = @{ param1 = $value }} | ConvertTo-Json
83+
84+
$null = $config_yaml | dsc config -p $params_json get
85+
$LASTEXITCODE | Should -Be 4
86+
}
87+
88+
It 'Input length is wrong for <type>' -TestCases @(
89+
@{ type = 'string'; value = 'hi' }
90+
@{ type = 'string'; value = 'hello' }
91+
@{ type = 'array'; value = @('hello', 'there') }
92+
@{ type = 'array'; value = @('hello', 'there', 'bye', 'now') }
93+
) {
94+
param($type, $value)
95+
96+
$config_yaml = @"
97+
`$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/10/config/document.json
98+
parameters:
99+
param1:
100+
type: $type
101+
minLength: 3
102+
maxLength: 3
103+
resources:
104+
- name: Echo
105+
type: Test/Echo
106+
properties:
107+
text: '[parameters(''param1'')]'
108+
"@
109+
$params_json = @{ parameters = @{ param1 = $value }} | ConvertTo-Json
110+
111+
$null = $config_yaml | dsc config -p $params_json get
112+
$LASTEXITCODE | Should -Be 4
113+
}
114+
115+
It 'Input number value is out of range for <min> and <max>' -TestCases @(
116+
@{ value = 42; min = 43; max = 44 }
117+
@{ value = 42; min = 41; max = 41 }
118+
@{ value = 42; min = 43; max = 41 }
119+
) {
120+
param($type, $value, $min, $max)
121+
122+
$config_yaml = @"
123+
`$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/10/config/document.json
124+
parameters:
125+
param1:
126+
type: int
127+
minValue: $min
128+
maxValue: $max
129+
resources:
130+
- name: Echo
131+
type: Test/Echo
132+
properties:
133+
text: '[parameters(''param1'')]'
134+
"@
135+
$params_json = @{ parameters = @{ param1 = $value }} | ConvertTo-Json
136+
137+
$null = $config_yaml | dsc config -p $params_json get
138+
$LASTEXITCODE | Should -Be 4
139+
}
140+
141+
It 'Input is not in the allowed value list for <type>' -TestCases @(
142+
@{ type = 'string'; value = 'hello'; allowed = @('world', 'planet') }
143+
@{ type = 'int'; value = 42; allowed = @(43, 44) }
144+
) {
145+
param($type, $value, $allowed)
146+
147+
$config_yaml = @"
148+
`$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/10/config/document.json
149+
parameters:
150+
param1:
151+
type: $type
152+
allowedValues: $($allowed | ConvertTo-Json -Compress)
153+
resources:
154+
- name: Echo
155+
type: Test/Echo
156+
properties:
157+
text: '[parameters(''param1'')]'
158+
"@
159+
$params_json = @{ parameters = @{ param1 = $value }} | ConvertTo-Json
160+
161+
$null = $config_yaml | dsc config -p $params_json get
162+
$LASTEXITCODE | Should -Be 4
163+
}
164+
165+
It 'Length constraint is incorrectly applied to <type> with <constraint>' -TestCases @(
166+
@{ type = 'int'; value = 42; constraint = 'minLength' }
167+
@{ type = 'int'; value = 42; constraint = 'maxLength' }
168+
@{ type = 'bool'; value = $true; constraint = 'minLength' }
169+
@{ type = 'bool'; value = $true; constraint = 'maxLength' }
170+
) {
171+
param($type, $value, $constraint)
172+
173+
$config_yaml = @"
174+
`$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/10/config/document.json
175+
parameters:
176+
param1:
177+
type: $type
178+
${constraint}: 3
179+
resources:
180+
- name: Echo
181+
type: Test/Echo
182+
properties:
183+
text: '[parameters(''param1'')]'
184+
"@
185+
$params_json = @{ parameters = @{ param1 = $value }} | ConvertTo-Json
186+
187+
$null = $config_yaml | dsc config -p $params_json get | ConvertFrom-Json
188+
$LASTEXITCODE | Should -Be 4
189+
}
190+
191+
It 'Default value is used when not provided' {
192+
$config_yaml = @"
193+
`$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/10/config/document.json
194+
parameters:
195+
param1:
196+
type: string
197+
defaultValue: 'hello'
198+
param2:
199+
type: int
200+
defaultValue: 7
201+
param3:
202+
type: bool
203+
defaultValue: false
204+
param4:
205+
type: array
206+
defaultValue: ['hello', 'world']
207+
resources:
208+
- name: Echo
209+
type: Test/Echo
210+
properties:
211+
text: '[concat(parameters(''param1''),'','',parameters(''param2''),'','',parameters(''param3''),'','',parameters(''param4''))]'
212+
"@
213+
214+
$out = $config_yaml | dsc config get | ConvertFrom-Json
215+
$LASTEXITCODE | Should -Be 0
216+
$out.results[0].result.actualState.text | Should -BeExactly '"hello",7,false,["hello","world"]'
217+
}
218+
}

dsc_lib/src/configure/config_doc.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,13 @@ pub struct Parameter {
3030
#[serde(rename = "allowedValues", skip_serializing_if = "Option::is_none")]
3131
pub allowed_values: Option<Vec<Value>>,
3232
#[serde(rename = "minValue", skip_serializing_if = "Option::is_none")]
33-
pub min_value: Option<Value>,
33+
pub min_value: Option<i64>,
3434
#[serde(rename = "maxValue", skip_serializing_if = "Option::is_none")]
35-
pub max_value: Option<Value>,
35+
pub max_value: Option<i64>,
3636
#[serde(rename = "minLength", skip_serializing_if = "Option::is_none")]
37-
pub min_length: Option<Value>,
37+
pub min_length: Option<i64>,
3838
#[serde(rename = "maxLength", skip_serializing_if = "Option::is_none")]
39-
pub max_length: Option<Value>,
39+
pub max_length: Option<i64>,
4040
#[serde(skip_serializing_if = "Option::is_none")]
4141
pub description: Option<String>,
4242
#[serde(skip_serializing_if = "Option::is_none")]

dsc_lib/src/configure/context.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
use serde_json::Value;
5+
use std::collections::HashMap;
6+
7+
pub struct Context {
8+
pub parameters: HashMap<String, Value>,
9+
pub _variables: HashMap<String, Value>,
10+
pub _outputs: HashMap<String, Value>, // This is eventually for References function to get output from resources
11+
}
12+
13+
impl Context {
14+
#[must_use]
15+
pub fn new() -> Self {
16+
Self {
17+
parameters: HashMap::new(),
18+
_variables: HashMap::new(),
19+
_outputs: HashMap::new(),
20+
}
21+
}
22+
}
23+
24+
impl Default for Context {
25+
fn default() -> Self {
26+
Self::new()
27+
}
28+
}

0 commit comments

Comments
 (0)