Skip to content

Commit 9a917a8

Browse files
SteveL-MSFTSteve Lee (POWERSHELL HE/HIM) (from Dev Box)CopilotCopilot
authored
Fix registry resource what-if for non-existing key (#1692)
* Fix registry resource what-if for non-existing key * Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> * Update how exit code is returned to work with code cov tools * Allow coverage threshold override label Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Steve Lee (POWERSHELL HE/HIM) (from Dev Box) <slee@ntdev.microsoft.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
1 parent 7773a93 commit 9a917a8

6 files changed

Lines changed: 66 additions & 34 deletions

File tree

.github/workflows/rust.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,7 @@ jobs:
487487
steps.coverage.outputs.has_rust_changes == 'true'
488488
&& steps.coverage.outputs.coverage_failed != 'true'
489489
&& steps.coverage.outputs.percentage < 70
490+
&& !contains(github.event.pull_request.labels.*.name, 'Ok-CodeCoverage')
490491
run: |
491492
Write-Error "Code coverage is ${{ steps.coverage.outputs.percentage }}%, which is below the 70% minimum threshold."
492493
exit 1

lib/dsc-lib-registry/locales/en-us.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ unsupportedValueDataType = "Unsupported registry value data type"
1818
whatIfCreateKey = "Key '%{subkey}' not found, would create it"
1919
whatIfDeleteValue = "Would delete value '%{value_name}'"
2020
whatIfDeleteSubkey = "Would delete subkey '%{subkey_name}'"
21+
whatIfDeleteNonexistingKey = "Key '%{subkey}' not found, would do nothing"
2122

2223
[offreg]
2324
loadFailed = "Failed to load offreg.dll"

lib/dsc-lib-registry/src/lib.rs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -316,13 +316,25 @@ impl RegistryHelper {
316316
return self.remove_offline();
317317
}
318318

319+
// Accumulate what-if metadata like set()
320+
let mut what_if_metadata: Vec<String> = Vec::new();
321+
319322
// For deleting a value, we need SetValue permission (KEY_SET_VALUE).
320323
// Try to open with the minimal required permission.
321324
// If that fails due to permission, try with AllAccess as a fallback.
322325
let (reg_key, _subkey) = match self.open(Security::SetValue) {
323326
Ok(reg_key) => reg_key,
324327
// handle NotFound error
325328
Err(RegistryError::RegistryKeyNotFound(_)) => {
329+
if self.what_if {
330+
what_if_metadata.push(t!("registry_helper.whatIfDeleteNonexistingKey", subkey = &self.config.key_path).to_string());
331+
return Ok(Some(Registry {
332+
key_path: self.config.key_path.clone(),
333+
value_name: self.config.value_name.clone(),
334+
metadata: Some(Metadata { what_if: Some(what_if_metadata) }),
335+
..Default::default()
336+
}));
337+
}
326338
return Ok(None);
327339
},
328340
Err(RegistryError::RegistryKey(key::Error::PermissionDenied(_, _))) => {
@@ -334,9 +346,6 @@ impl RegistryHelper {
334346
Err(e) => return self.handle_error_or_what_if(e),
335347
};
336348

337-
// Accumulate what-if metadata like set()
338-
let mut what_if_metadata: Vec<String> = Vec::new();
339-
340349
if let Some(value_name) = &self.config.value_name {
341350
if self.what_if {
342351
what_if_metadata.push(t!("registry_helper.whatIfDeleteValue", value_name = value_name).to_string());
@@ -385,6 +394,7 @@ impl RegistryHelper {
385394
Err(e) => return self.handle_error_or_what_if(RegistryError::RegistryKey(e)),
386395
}
387396
}
397+
388398
Ok(None)
389399
}
390400

lib/dsc-lib/src/dscresources/invoke_result.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,6 @@ pub struct ResolveResult {
184184
}
185185

186186
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)]
187-
#[serde(deny_unknown_fields)]
188187
#[dsc_repo_schema(base_name = "delete", folder_path = "outputs/resource")]
189188
pub struct DeleteResult {
190189
/// The return from the resource by the Delete method with what-if simulation.

resources/registry/src/main.rs

Lines changed: 34 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use clap::Parser;
1212
use dsc_lib_registry::{config::Registry, RegistryHelper};
1313
use rust_i18n::t;
1414
use schemars::schema_for;
15-
use std::process::exit;
15+
use std::process::ExitCode;
1616
use tracing::{error, trace};
1717
use tracing_subscriber::{filter::LevelFilter, prelude::__tracing_subscriber_SubscriberExt, EnvFilter, Layer};
1818
use types::RegistryList;
@@ -24,12 +24,12 @@ mod types;
2424

2525
rust_i18n::i18n!("locales", fallback = "en-us");
2626

27-
const EXIT_SUCCESS: i32 = 0;
28-
const EXIT_INVALID_INPUT: i32 = 2;
29-
const EXIT_REGISTRY_ERROR: i32 = 3;
27+
const EXIT_SUCCESS: u8 = 0;
28+
const EXIT_INVALID_INPUT: u8 = 2;
29+
const EXIT_REGISTRY_ERROR: u8 = 3;
3030

3131
#[allow(clippy::too_many_lines)]
32-
fn main() {
32+
fn main() -> ExitCode {
3333
#[cfg(debug_assertions)]
3434
check_debug();
3535

@@ -45,17 +45,17 @@ fn main() {
4545
AdapterSubCommand::Set { input, adapted_resource } => {
4646
if let Err(e) = adapter_set(&input, &adapted_resource) {
4747
error!("{e}");
48-
exit(EXIT_REGISTRY_ERROR);
48+
return ExitCode::from(EXIT_REGISTRY_ERROR);
4949
}
50-
exit(EXIT_SUCCESS);
50+
return ExitCode::from(EXIT_SUCCESS);
5151
},
5252
AdapterSubCommand::Export { input, adapted_resource } => {
5353
adapter_export(&input, &adapted_resource)
5454
},
5555
AdapterSubCommand::Schema => {
5656
let schema = schema_for!(AdaptedRegistryValue);
5757
println!("{}", serde_json::to_string(&schema).unwrap());
58-
exit(EXIT_SUCCESS);
58+
return ExitCode::from(EXIT_SUCCESS);
5959
}
6060
};
6161
match result {
@@ -64,7 +64,7 @@ fn main() {
6464
},
6565
Err(err) => {
6666
error!("{err}");
67-
exit(EXIT_INVALID_INPUT);
67+
return ExitCode::from(EXIT_INVALID_INPUT);
6868
}
6969
}
7070
},
@@ -85,13 +85,15 @@ fn main() {
8585
ConfigSubCommand::Get{input, list} => {
8686
trace!("Get input: {input}");
8787
let mut output = RegistryList { registry_entries: vec![], registry_file_path: None };
88-
let reg_list = import_input(&input, list);
88+
let Ok(reg_list) = import_input(&input, list) else {
89+
return ExitCode::from(EXIT_INVALID_INPUT);
90+
};
8991
for reg in reg_list.registry_entries {
9092
let reg_helper = match RegistryHelper::new_from_registry(&reg) {
9193
Ok(helper) => helper,
9294
Err(err) => {
9395
error!("{err}");
94-
exit(EXIT_INVALID_INPUT);
96+
return ExitCode::from(EXIT_INVALID_INPUT);
9597
}
9698
};
9799
match reg_helper.get() {
@@ -101,29 +103,31 @@ fn main() {
101103
} else {
102104
let json = serde_json::to_string(&reg_config).unwrap();
103105
println!("{json}");
104-
exit(EXIT_SUCCESS);
106+
return ExitCode::from(EXIT_SUCCESS);
105107
}
106108
},
107109
Err(err) => {
108110
error!("{err}");
109-
exit(EXIT_REGISTRY_ERROR);
111+
return ExitCode::from(EXIT_REGISTRY_ERROR);
110112
}
111113
}
112114
}
113115
let json = serde_json::to_string(&output).unwrap();
114116
println!("{json}");
115-
exit(EXIT_SUCCESS);
117+
return ExitCode::from(EXIT_SUCCESS);
116118
},
117119
ConfigSubCommand::Set{input, list, what_if} => {
118120
trace!("Set input: {input}, what_if: {what_if}");
119121
let mut output = RegistryList { registry_entries: vec![], registry_file_path: None };
120-
let reg_list = import_input(&input, list);
122+
let Ok(reg_list) = import_input(&input, list) else {
123+
return ExitCode::from(EXIT_INVALID_INPUT);
124+
};
121125
for reg in reg_list.registry_entries {
122126
let mut reg_helper = match RegistryHelper::new_from_registry(&reg) {
123127
Ok(helper) => helper,
124128
Err(err) => {
125129
error!("{err}");
126-
exit(EXIT_INVALID_INPUT);
130+
return ExitCode::from(EXIT_INVALID_INPUT);
127131
}
128132
};
129133
if what_if { reg_helper.enable_what_if(); }
@@ -136,14 +140,14 @@ fn main() {
136140
} else {
137141
let json = serde_json::to_string(&reg_config).unwrap();
138142
println!("{json}");
139-
exit(EXIT_SUCCESS);
143+
return ExitCode::from(EXIT_SUCCESS);
140144
}
141145
}
142146
},
143147
Ok(None) => {},
144148
Err(err) => {
145149
error!("{err}");
146-
exit(EXIT_REGISTRY_ERROR);
150+
return ExitCode::from(EXIT_REGISTRY_ERROR);
147151
}
148152
}
149153
continue;
@@ -156,32 +160,32 @@ fn main() {
156160
} else {
157161
let json = serde_json::to_string(&config).unwrap();
158162
println!("{json}");
159-
exit(EXIT_SUCCESS);
163+
return ExitCode::from(EXIT_SUCCESS);
160164
}
161165
}
162166
if !list {
163-
exit(EXIT_SUCCESS);
167+
return ExitCode::from(EXIT_SUCCESS);
164168
}
165169
},
166170
Err(err) => {
167171
error!("{err}");
168-
exit(EXIT_REGISTRY_ERROR);
172+
return ExitCode::from(EXIT_REGISTRY_ERROR);
169173
}
170174
}
171175
}
172176
if what_if {
173177
let json = serde_json::to_string(&output).unwrap();
174178
println!("{json}");
175179
}
176-
exit(EXIT_SUCCESS);
180+
return ExitCode::from(EXIT_SUCCESS);
177181
},
178182
ConfigSubCommand::Delete{input, what_if} => {
179183
trace!("Delete input: {input}, what_if: {what_if}");
180184
let mut reg_helper = match RegistryHelper::new_from_json(&input) {
181185
Ok(reg_helper) => reg_helper,
182186
Err(err) => {
183187
error!("{err}");
184-
exit(EXIT_INVALID_INPUT);
188+
return ExitCode::from(EXIT_INVALID_INPUT);
185189
}
186190
};
187191
if what_if { reg_helper.enable_what_if(); }
@@ -193,7 +197,7 @@ fn main() {
193197
Ok(None) => {},
194198
Err(err) => {
195199
error!("{err}");
196-
exit(EXIT_REGISTRY_ERROR);
200+
return ExitCode::from(EXIT_REGISTRY_ERROR);
197201
}
198202
}
199203
},
@@ -210,10 +214,10 @@ fn main() {
210214
},
211215
}
212216

213-
exit(EXIT_SUCCESS);
217+
ExitCode::from(EXIT_SUCCESS)
214218
}
215219

216-
fn import_input(input: &str, list: bool) -> RegistryList {
220+
fn import_input(input: &str, list: bool) -> Result<RegistryList, ExitCode> {
217221
if list {
218222
match serde_json::from_str::<RegistryList>(input) {
219223
Ok(mut reg_list) => {
@@ -225,19 +229,19 @@ fn import_input(input: &str, list: bool) -> RegistryList {
225229
}
226230
}
227231
}
228-
reg_list
232+
Ok(reg_list)
229233
},
230234
Err(err) => {
231235
error!("{err}");
232-
exit(EXIT_INVALID_INPUT);
236+
Err(ExitCode::from(EXIT_INVALID_INPUT))
233237
}
234238
}
235239
} else {
236240
match serde_json::from_str::<Registry>(input) {
237-
Ok(reg) => RegistryList { registry_entries: vec![reg], registry_file_path: None },
241+
Ok(reg) => Ok(RegistryList { registry_entries: vec![reg], registry_file_path: None }),
238242
Err(err) => {
239243
error!("{err}");
240-
exit(EXIT_INVALID_INPUT);
244+
Err(ExitCode::from(EXIT_INVALID_INPUT))
241245
}
242246
}
243247
}

resources/registry/tests/registry.config.whatif.tests.ps1

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,4 +200,21 @@ Describe 'registry config whatif tests' {
200200
# For delete what-if, payload should only include keyPath (and optionally valueName when deleting a value)
201201
($result.psobject.properties | Where-Object { $_.Name -ne '_metadata' } | Measure-Object).Count | Should -Be 1
202202
}
203+
204+
205+
It 'Removing non-existing key' -Skip:(!$IsWindows) {
206+
$after_config_yaml = @'
207+
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
208+
resources:
209+
- name: Reg 1
210+
type: Microsoft.Windows/Registry
211+
properties:
212+
keyPath: HKCU\1\2\NonExisting
213+
_exist: false
214+
'@
215+
$out = dsc -l trace config set --what-if --input $after_config_yaml 2>$TestDrive/error.log | ConvertFrom-Json
216+
$LASTEXITCODE | Should -Be 0 -Because (Get-Content -Path $TestDrive/error.log -Raw)
217+
$out.results.result[0].afterState.keyPath | Should -BeExactly 'HKCU\1\2\NonExisting'
218+
$out.results.executionInformation.whatIf[0] | Should -Match "Key 'HKCU\\1\\2\\NonExisting' not found, would do nothing"
219+
}
203220
}

0 commit comments

Comments
 (0)