Skip to content

Commit 849a35e

Browse files
committed
add whatif as a separate object in manifest
1 parent a37e30f commit 849a35e

File tree

9 files changed

+58
-62
lines changed

9 files changed

+58
-62
lines changed

dsc/src/subcommand.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ pub fn config(subcommand: &ConfigSubCommand, parameters: &Option<String>, stdin:
250250

251251
if let ConfigSubCommand::Set { what_if , .. } = subcommand {
252252
if *what_if {
253-
configurator.context.execution_type = ExecutionKind::WhatIfDSC;
253+
configurator.context.execution_type = ExecutionKind::WhatIf;
254254
}
255255
};
256256

@@ -516,7 +516,7 @@ fn list_resources(dsc: &mut DscManager, resource_name: &Option<String>, adapter_
516516
(Capability::Get, "g"),
517517
(Capability::Set, "s"),
518518
(Capability::SetHandlesExist, "x"),
519-
(Capability::SetHandlesWhatIf, "w"),
519+
(Capability::WhatIf, "w"),
520520
(Capability::Test, "t"),
521521
(Capability::Delete, "d"),
522522
(Capability::Export, "e"),

dsc_lib/src/configure/config_doc.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,7 @@ pub enum Operation {
3030
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
3131
pub enum ExecutionKind {
3232
Actual,
33-
// differentiate internally whether what-if should be processed by the resource or dsc engine
34-
#[serde(rename = "WhatIf")]
35-
WhatIfDSC,
36-
#[serde(rename = "WhatIf")]
37-
WhatIfResource,
33+
WhatIf,
3834
}
3935

4036
#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]

dsc_lib/src/configure/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ impl Configurator {
318318
set_result = dsc_resource.set(&desired, skip_test, &self.context.execution_type)?;
319319
end_datetime = chrono::Local::now();
320320
} else if dsc_resource.capabilities.contains(&Capability::Delete) {
321-
if self.context.execution_type == ExecutionKind::WhatIfDSC {
321+
if self.context.execution_type == ExecutionKind::WhatIf {
322322
// TODO: add delete what-if support
323323
return Err(DscError::NotSupported("What-if execution not supported for delete".to_string()));
324324
}

dsc_lib/src/discovery/command_discovery.rs

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
use crate::discovery::discovery_trait::ResourceDiscovery;
55
use crate::discovery::convert_wildcard_to_regex;
66
use crate::dscresources::dscresource::{Capability, DscResource, ImplementedAs};
7-
use crate::dscresources::resource_manifest::{import_manifest, validate_semver, ArgKind, Kind, ResourceManifest};
7+
use crate::dscresources::resource_manifest::{import_manifest, validate_semver, Kind, ResourceManifest};
88
use crate::dscresources::command_resource::invoke_command;
99
use crate::dscerror::DscError;
1010
use indicatif::ProgressStyle;
@@ -449,14 +449,9 @@ fn load_manifest(path: &Path) -> Result<DscResource, DscError> {
449449
if set.handles_exist == Some(true) {
450450
capabilities.push(Capability::SetHandlesExist);
451451
}
452-
if let Some(arg_values) = &set.args {
453-
for arg in arg_values {
454-
if let &ArgKind::WhatIf { .. } = arg {
455-
capabilities.push(Capability::SetHandlesWhatIf);
456-
break;
457-
}
458-
}
459-
}
452+
}
453+
if manifest.whatif.is_some() {
454+
capabilities.push(Capability::WhatIf);
460455
}
461456
if manifest.test.is_some() {
462457
capabilities.push(Capability::Test);

dsc_lib/src/dscresources/command_resource.rs

Lines changed: 32 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ pub fn invoke_get(resource: &ResourceManifest, cwd: &str, filter: &str) -> Resul
5050
let Some(get) = &resource.get else {
5151
return Err(DscError::NotImplemented("get".to_string()));
5252
};
53-
let args = process_args(&get.args, filter, &ExecutionKind::Actual);
53+
let args = process_args(&get.args, filter);
5454
if !filter.is_empty() {
5555
verify_json(resource, cwd, filter)?;
5656
command_input = get_command_input(&get.input, filter)?;
@@ -95,17 +95,30 @@ pub fn invoke_get(resource: &ResourceManifest, cwd: &str, filter: &str) -> Resul
9595
#[allow(clippy::too_many_lines)]
9696
pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_test: bool, execution_type: &ExecutionKind) -> Result<SetResult, DscError> {
9797
// TODO: support import resources
98-
99-
let Some(set) = &resource.set else {
100-
return Err(DscError::NotImplemented("set".to_string()));
98+
let operation_type: String;
99+
let mut is_synthetic_what_if = false;
100+
let set = match execution_type {
101+
ExecutionKind::Actual => {
102+
operation_type = "set".to_string();
103+
resource.set.clone().ok_or(DscError::NotImplemented("set".to_string()))?
104+
},
105+
ExecutionKind::WhatIf => {
106+
operation_type = "whatif".to_string();
107+
if let Some(whatif) = &resource.whatif {
108+
whatif.clone()
109+
} else {
110+
is_synthetic_what_if = true;
111+
resource.set.clone().ok_or(DscError::NotImplemented("set".to_string()))?
112+
}
113+
}
101114
};
102115
verify_json(resource, cwd, desired)?;
103116

104117
// if resource doesn't implement a pre-test, we execute test first to see if a set is needed
105118
if !skip_test && set.pre_test != Some(true) {
106119
info!("No pretest, invoking test {}", &resource.resource_type);
107120
let test_result = invoke_test(resource, cwd, desired)?;
108-
if execution_type == &ExecutionKind::WhatIfDSC {
121+
if is_synthetic_what_if {
109122
return Ok(test_result.into());
110123
}
111124
let (in_desired_state, actual_state) = match test_result {
@@ -130,17 +143,17 @@ pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_te
130143
}
131144
}
132145

133-
if ExecutionKind::WhatIfDSC == *execution_type {
146+
if is_synthetic_what_if {
134147
return Err(DscError::NotImplemented("cannot process what-if execution type, as resource implements pre-test and does not support what-if".to_string()));
135148
}
136149

137150
let Some(get) = &resource.get else {
138151
return Err(DscError::NotImplemented("get".to_string()));
139152
};
140-
let args = process_args(&get.args, desired, &ExecutionKind::Actual);
153+
let args = process_args(&get.args, desired);
141154
let command_input = get_command_input(&get.input, desired)?;
142155

143-
info!("Getting current state for set by invoking get {} using {}", &resource.resource_type, &get.executable);
156+
info!("Getting current state for {} by invoking get {} using {}", operation_type, &resource.resource_type, &get.executable);
144157
let (exit_code, stdout, stderr) = invoke_command(&get.executable, args, command_input.stdin.as_deref(), Some(cwd), command_input.env)?;
145158

146159
if resource.kind == Some(Kind::Resource) {
@@ -157,7 +170,7 @@ pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_te
157170

158171
let mut env: Option<HashMap<String, String>> = None;
159172
let mut input_desired: Option<&str> = None;
160-
let args = process_args(&set.args, desired, execution_type);
173+
let args = process_args(&set.args, desired);
161174
match &set.input {
162175
Some(InputKind::Env) => {
163176
env = Some(json_to_hashmap(desired)?);
@@ -170,21 +183,21 @@ pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_te
170183
},
171184
}
172185

173-
info!("Invoking set '{}' using '{}'", &resource.resource_type, &set.executable);
186+
info!("Invoking {} '{}' using '{}'", operation_type, &resource.resource_type, &set.executable);
174187
let (exit_code, stdout, stderr) = invoke_command(&set.executable, args, input_desired, Some(cwd), env)?;
175188

176189
match set.returns {
177190
Some(ReturnKind::State) => {
178191

179192
if resource.kind == Some(Kind::Resource) {
180-
debug!("Verifying output of set '{}' using '{}'", &resource.resource_type, &set.executable);
193+
debug!("Verifying output of {} '{}' using '{}'", operation_type, &resource.resource_type, &set.executable);
181194
verify_json(resource, cwd, &stdout)?;
182195
}
183196

184197
let actual_value: Value = match serde_json::from_str(&stdout){
185198
Result::Ok(r) => {r},
186199
Result::Err(err) => {
187-
return Err(DscError::Operation(format!("Failed to parse json from set {}|{}|{} -> {err}", &set.executable, stdout, stderr)))
200+
return Err(DscError::Operation(format!("Failed to parse json from {} '{}'|'{}'|'{}' -> {err}", operation_type, &set.executable, stdout, stderr)))
188201
}
189202
};
190203

@@ -260,7 +273,7 @@ pub fn invoke_test(resource: &ResourceManifest, cwd: &str, expected: &str) -> Re
260273

261274
verify_json(resource, cwd, expected)?;
262275

263-
let args = process_args(&test.args, expected, &ExecutionKind::Actual);
276+
let args = process_args(&test.args, expected);
264277
let command_input = get_command_input(&test.input, expected)?;
265278

266279
info!("Invoking test '{}' using '{}'", &resource.resource_type, &test.executable);
@@ -374,7 +387,7 @@ pub fn invoke_delete(resource: &ResourceManifest, cwd: &str, filter: &str) -> Re
374387

375388
verify_json(resource, cwd, filter)?;
376389

377-
let args = process_args(&delete.args, filter, &ExecutionKind::Actual);
390+
let args = process_args(&delete.args, filter);
378391
let command_input = get_command_input(&delete.input, filter)?;
379392

380393
info!("Invoking delete '{}' using '{}'", &resource.resource_type, &delete.executable);
@@ -405,7 +418,7 @@ pub fn invoke_validate(resource: &ResourceManifest, cwd: &str, config: &str) ->
405418
return Err(DscError::NotImplemented("validate".to_string()));
406419
};
407420

408-
let args = process_args(&validate.args, config, &ExecutionKind::Actual);
421+
let args = process_args(&validate.args, config);
409422
let command_input = get_command_input(&validate.input, config)?;
410423

411424
info!("Invoking validate '{}' using '{}'", &resource.resource_type, &validate.executable);
@@ -480,9 +493,9 @@ pub fn invoke_export(resource: &ResourceManifest, cwd: &str, input: Option<&str>
480493
command_input = get_command_input(&export.input, input)?;
481494
}
482495

483-
args = process_args(&export.args, input, &ExecutionKind::Actual);
496+
args = process_args(&export.args, input);
484497
} else {
485-
args = process_args(&export.args, "", &ExecutionKind::Actual);
498+
args = process_args(&export.args, "");
486499
}
487500

488501
let (_exit_code, stdout, stderr) = invoke_command(&export.executable, args, command_input.stdin.as_deref(), Some(cwd), command_input.env)?;
@@ -527,7 +540,7 @@ pub fn invoke_resolve(resource: &ResourceManifest, cwd: &str, input: &str) -> Re
527540
return Err(DscError::Operation(format!("Resolve is not supported by resource {}", &resource.resource_type)));
528541
};
529542

530-
let args = process_args(&resolve.args, input, &ExecutionKind::Actual);
543+
let args = process_args(&resolve.args, input);
531544
let command_input = get_command_input(&resolve.input, input)?;
532545

533546
info!("Invoking resolve '{}' using '{}'", &resource.resource_type, &resolve.executable);
@@ -619,7 +632,7 @@ pub fn invoke_command(executable: &str, args: Option<Vec<String>>, input: Option
619632
Ok((exit_code, stdout, cleaned_stderr))
620633
}
621634

622-
fn process_args(args: &Option<Vec<ArgKind>>, value: &str, execution_type: &ExecutionKind) -> Option<Vec<String>> {
635+
fn process_args(args: &Option<Vec<ArgKind>>, value: &str) -> Option<Vec<String>> {
623636
let Some(arg_values) = args else {
624637
debug!("No args to process");
625638
return None;
@@ -639,11 +652,6 @@ fn process_args(args: &Option<Vec<ArgKind>>, value: &str, execution_type: &Execu
639652
processed_args.push(json_input_arg.clone());
640653
processed_args.push(value.to_string());
641654
},
642-
ArgKind::WhatIf { what_if_input_arg } => {
643-
if execution_type == &ExecutionKind::WhatIfResource {
644-
processed_args.push(what_if_input_arg.clone());
645-
}
646-
}
647655
}
648656
}
649657

dsc_lib/src/dscresources/dscresource.rs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,8 @@ pub enum Capability {
5050
Set,
5151
/// The resource supports the `_exist` property directly.
5252
SetHandlesExist,
53-
/// The resource supports the `what-if` execution type directly.
54-
SetHandlesWhatIf,
53+
/// The resource supports simulating configuration directly.
54+
WhatIf,
5555
/// The resource supports validating configuration.
5656
Test,
5757
/// The resource supports deleting configuration.
@@ -211,13 +211,7 @@ impl Invoke for DscResource {
211211
return Err(DscError::MissingManifest(self.type_name.clone()));
212212
};
213213
let resource_manifest = import_manifest(manifest.clone())?;
214-
let execution = if self.capabilities.contains(&Capability::SetHandlesWhatIf) && execution_type == &ExecutionKind::WhatIfDSC {
215-
ExecutionKind::WhatIfResource
216-
}
217-
else {
218-
execution_type.clone()
219-
};
220-
command_resource::invoke_set(&resource_manifest, &self.directory, desired, skip_test, &execution)
214+
command_resource::invoke_set(&resource_manifest, &self.directory, desired, skip_test, execution_type)
221215
},
222216
}
223217
}

dsc_lib/src/dscresources/resource_manifest.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ pub struct ResourceManifest {
4040
/// Details how to call the Set method of the resource.
4141
#[serde(skip_serializing_if = "Option::is_none")]
4242
pub set: Option<SetMethod>,
43+
/// Details how to call the `WhatIf` method of the resource.
44+
#[serde(skip_serializing_if = "Option::is_none")]
45+
pub whatif: Option<SetMethod>,
4346
/// Details how to call the Test method of the resource.
4447
#[serde(skip_serializing_if = "Option::is_none")]
4548
pub test: Option<TestMethod>,
@@ -103,11 +106,6 @@ pub enum ArgKind {
103106
/// Indicates if argument is mandatory which will pass an empty string if no JSON input is provided. Default is false.
104107
mandatory: Option<bool>,
105108
},
106-
WhatIf {
107-
/// The argument that serves as the what-if switch.
108-
#[serde(rename = "whatIfSwitchArg")]
109-
what_if_input_arg: String,
110-
}
111109
}
112110

113111
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]

tools/dsctest/dscwhatif.dsc.resource.json

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,21 @@
55
"get": {
66
"executable": "dsctest",
77
"args": [
8-
"what-if"
8+
"whatif"
99
]
1010
},
1111
"set": {
1212
"executable": "dsctest",
1313
"args": [
14-
"what-if",
15-
{
16-
"whatIfSwitchArg": "--what-if"
17-
}
14+
"whatif"
15+
],
16+
"return": "state"
17+
},
18+
"whatif": {
19+
"executable": "dsctest",
20+
"args": [
21+
"whatif",
22+
"-w"
1823
],
1924
"return": "state"
2025
},

tools/dsctest/src/args.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@ pub enum SubCommand {
5656
#[clap(name = "trace", about = "The trace level")]
5757
Trace,
5858

59-
#[clap(name = "what-if", about = "Check if it is a what-if operation")]
59+
#[clap(name = "whatif", about = "Check if it is a whatif operation")]
6060
WhatIf {
61-
#[clap(name = "what-if", short, long, help = "Run as a what-if executionType instead of actual executionType")]
61+
#[clap(name = "whatif", short, long, help = "Run as a whatif executionType instead of actual executionType")]
6262
what_if: bool,
6363
}
6464
}

0 commit comments

Comments
 (0)