Skip to content

Commit 38a35b0

Browse files
committed
Enable resources to return _restartRequired metadata
1 parent 39eade5 commit 38a35b0

File tree

7 files changed

+105
-10
lines changed

7 files changed

+105
-10
lines changed

dsc/src/args.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,4 +289,5 @@ pub enum SchemaType {
289289
ExtensionManifest,
290290
ExtensionDiscoverResult,
291291
FunctionDefinition,
292+
RestartRequired
292293
}

dsc/src/util.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@ use dsc_lib::extensions::discover::DiscoverResult;
88
use dsc_lib::extensions::extension_manifest::ExtensionManifest;
99
use dsc_lib::{
1010
configure::{
11-
config_doc::Configuration,
11+
config_doc::{
12+
Configuration,
13+
RestartRequired,
14+
},
1215
config_result::{
1316
ConfigurationGetResult,
1417
ConfigurationSetResult,
@@ -187,6 +190,9 @@ pub fn get_schema(schema: SchemaType) -> RootSchema {
187190
},
188191
SchemaType::FunctionDefinition => {
189192
schema_for!(FunctionDefinition)
193+
},
194+
SchemaType::RestartRequired => {
195+
schema_for!(RestartRequired)
190196
}
191197
}
192198
}

dsc/tests/dsc_metadata.tests.ps1

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,56 @@ Describe 'metadata tests' {
7171
$out.results[0].metadata.Microsoft.DSC | Should -BeNullOrEmpty
7272
(Get-Content $TestDrive/error.log) | Should -BeLike "*WARN*Resource returned '_metadata' property 'Microsoft.DSC' which is ignored*"
7373
}
74+
75+
It 'resource returning _restartRequired metadata is handled' {
76+
$configYaml = @'
77+
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
78+
resources:
79+
- name: one
80+
type: Test/Metadata
81+
properties:
82+
_metadata:
83+
_restartRequired:
84+
- system: mySystem
85+
- service: myService
86+
- name: two
87+
type: Test/Metadata
88+
properties:
89+
_metadata:
90+
_restartRequired:
91+
- service: sshd
92+
- name: three
93+
type: Test/Metadata
94+
properties:
95+
_metadata:
96+
_restartRequired:
97+
- process:
98+
name: myProcess
99+
id: 1234
100+
- process:
101+
name: anotherProcess
102+
id: 5678
103+
'@
104+
$out = dsc config get -i $configYaml 2>$TestDrive/error.log | ConvertFrom-Json
105+
$LASTEXITCODE | Should -Be 0
106+
$out.results.count | Should -Be 3
107+
$out.results[0].metadata._restartRequired.count | Should -Be 2
108+
$out.results[0].metadata._restartRequired[0].system | Should -BeExactly 'mySystem'
109+
$out.results[0].metadata._restartRequired[1].service | Should -BeExactly 'myService'
110+
$out.results[1].metadata._restartRequired.count | Should -Be 1
111+
$out.results[1].metadata._restartRequired[0].service | Should -BeExactly 'sshd'
112+
$out.results[2].metadata._restartRequired.count | Should -Be 2
113+
$out.results[2].metadata._restartRequired[0].process.name | Should -BeExactly 'myProcess'
114+
$out.results[2].metadata._restartRequired[0].process.id | Should -Be 1234
115+
$out.results[2].metadata._restartRequired[1].process.name | Should -BeExactly 'anotherProcess'
116+
$out.results[2].metadata._restartRequired[1].process.id | Should -Be 5678
117+
$out.metadata.'Microsoft.DSC'.restartRequired.count | Should -Be 5
118+
$out.metadata.'Microsoft.DSC'.restartRequired[0].system | Should -BeExactly 'mySystem'
119+
$out.metadata.'Microsoft.DSC'.restartRequired[1].service | Should -BeExactly 'myService'
120+
$out.metadata.'Microsoft.DSC'.restartRequired[2].service | Should -BeExactly 'sshd'
121+
$out.metadata.'Microsoft.DSC'.restartRequired[3].process.name | Should -BeExactly 'myProcess'
122+
$out.metadata.'Microsoft.DSC'.restartRequired[3].process.id | Should -Be 1234
123+
$out.metadata.'Microsoft.DSC'.restartRequired[4].process.name | Should -BeExactly 'anotherProcess'
124+
$out.metadata.'Microsoft.DSC'.restartRequired[4].process.id | Should -Be 5678
125+
}
74126
}

dsc_lib/locales/en-us.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ invokeExpression = "Invoke property expression for %{name}: %{value}"
6969
propertyNotString = "Property '%{name}' with value '%{value}' is not a string"
7070
metadataMicrosoftDscIgnored = "Resource returned '_metadata' property 'Microsoft.DSC' which is ignored"
7171
metadataNotObject = "Resource returned '_metadata' property which is not an object"
72+
metadataRestartRequiredInvalid = "Resource returned '_metadata' property '_restartRequired' which contains invalid value: %{value}"
7273

7374
[discovery.commandDiscovery]
7475
couldNotReadSetting = "Could not read 'resourcePath' setting"

dsc_lib/src/configure/config_doc.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,21 @@ pub enum ExecutionKind {
3434
WhatIf,
3535
}
3636

37+
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
38+
#[serde(rename_all = "camelCase")]
39+
pub struct Process {
40+
pub name: String,
41+
pub id: u32,
42+
}
43+
44+
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
45+
#[serde(rename_all = "camelCase")]
46+
pub enum RestartRequired {
47+
System(String),
48+
Service(String),
49+
Process(Process),
50+
}
51+
3752
#[derive(Debug, Default, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
3853
pub struct MicrosoftDscMetadata {
3954
/// Version of DSC
@@ -57,6 +72,9 @@ pub struct MicrosoftDscMetadata {
5772
/// The security context of the configuration operation, can be specified to be required
5873
#[serde(rename = "securityContext", skip_serializing_if = "Option::is_none")]
5974
pub security_context: Option<SecurityContextKind>,
75+
/// Indicates what needs to be restarted after the configuration operation
76+
#[serde(rename = "restartRequired", skip_serializing_if = "Option::is_none")]
77+
pub restart_required: Option<Vec<RestartRequired>>,
6078
}
6179

6280
impl MicrosoftDscMetadata {

dsc_lib/src/configure/context.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use security_context_lib::{get_security_context, SecurityContext};
77
use serde_json::{Map, Value};
88
use std::{collections::HashMap, path::PathBuf};
99

10-
use super::config_doc::{DataType, SecurityContextKind};
10+
use super::config_doc::{DataType, RestartRequired, SecurityContextKind};
1111

1212
pub struct Context {
1313
pub execution_type: ExecutionKind,
@@ -17,6 +17,7 @@ pub struct Context {
1717
pub security_context: SecurityContextKind,
1818
pub variables: Map<String, Value>,
1919
pub start_datetime: DateTime<Local>,
20+
pub restart_required: Option<Vec<RestartRequired>>,
2021
}
2122

2223
impl Context {
@@ -33,6 +34,7 @@ impl Context {
3334
},
3435
variables: Map::new(),
3536
start_datetime: chrono::Local::now(),
37+
restart_required: None,
3638
}
3739
}
3840
}

dsc_lib/src/configure/mod.rs

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

44
use crate::configure::config_doc::{ExecutionKind, Metadata, Resource};
5-
use crate::configure::parameters::Input;
5+
use crate::configure::{config_doc::RestartRequired, parameters::Input};
66
use crate::dscerror::DscError;
77
use crate::dscresources::invoke_result::ExportResult;
88
use crate::dscresources::{
@@ -87,17 +87,17 @@ pub fn add_resource_export_results_to_configuration(resource: &DscResource, conf
8787
other: Map::new(),
8888
};
8989
if let Some(security_context) = props.remove("_securityContext") {
90-
let context: SecurityContextKind = serde_json::from_value(security_context)?;
90+
let security_context: SecurityContextKind = serde_json::from_value(security_context)?;
9191
metadata.microsoft = Some(
9292
MicrosoftDscMetadata {
93-
security_context: Some(context),
93+
security_context: Some(security_context),
9494
..Default::default()
9595
}
9696
);
9797
}
9898
r.properties = escape_property_values(&props)?;
9999
let mut properties = serde_json::to_value(&r.properties)?;
100-
get_metadata_from_result(&mut properties, &mut metadata)?;
100+
get_metadata_from_result(None, &mut properties, &mut metadata)?;
101101
r.properties = Some(properties.as_object().cloned().unwrap_or_default());
102102
r.metadata = if metadata.microsoft.is_some() || !metadata.other.is_empty() {
103103
Some(metadata)
@@ -225,14 +225,24 @@ fn check_security_context(metadata: Option<&Metadata>) -> Result<(), DscError> {
225225
Ok(())
226226
}
227227

228-
fn get_metadata_from_result(result: &mut Value, metadata: &mut Metadata) -> Result<(), DscError> {
228+
fn get_metadata_from_result(mut context: Option<&mut Context>, result: &mut Value, metadata: &mut Metadata) -> Result<(), DscError> {
229229
if let Some(metadata_value) = result.get("_metadata") {
230230
if let Some(metadata_map) = metadata_value.as_object() {
231231
for (key, value) in metadata_map {
232232
if key.starts_with("Microsoft.DSC") {
233233
warn!("{}", t!("configure.mod.metadataMicrosoftDscIgnored", key = key));
234234
continue;
235235
}
236+
if let Some(ref mut context) = context {
237+
if key == "_restartRequired" {
238+
if let Some(restart_required) = serde_json::from_value::<Vec<RestartRequired>>(value.clone()).ok() {
239+
context.restart_required.get_or_insert_with(Vec::new).extend(restart_required);
240+
} else {
241+
warn!("{}", t!("configure.mod.metadataRestartRequiredInvalid", value = value));
242+
continue;
243+
}
244+
}
245+
}
236246
metadata.other.insert(key.clone(), value.clone());
237247
}
238248
} else {
@@ -335,7 +345,7 @@ impl Configurator {
335345
match &mut get_result {
336346
GetResult::Resource(ref mut resource_result) => {
337347
self.context.references.insert(format!("{}:{}", resource.resource_type, resource.name), serde_json::to_value(&resource_result.actual_state)?);
338-
get_metadata_from_result(&mut resource_result.actual_state, &mut metadata)?;
348+
get_metadata_from_result(Some(&mut self.context), &mut resource_result.actual_state, &mut metadata)?;
339349
},
340350
GetResult::Group(group) => {
341351
let mut results = Vec::<Value>::new();
@@ -488,7 +498,7 @@ impl Configurator {
488498
match &mut set_result {
489499
SetResult::Resource(resource_result) => {
490500
self.context.references.insert(format!("{}:{}", resource.resource_type, resource.name), serde_json::to_value(&resource_result.after_state)?);
491-
get_metadata_from_result(&mut resource_result.after_state, &mut metadata)?;
501+
get_metadata_from_result(Some(&mut self.context), &mut resource_result.after_state, &mut metadata)?;
492502
},
493503
SetResult::Group(group) => {
494504
let mut results = Vec::<Value>::new();
@@ -558,7 +568,7 @@ impl Configurator {
558568
match &mut test_result {
559569
TestResult::Resource(resource_test_result) => {
560570
self.context.references.insert(format!("{}:{}", resource.resource_type, resource.name), serde_json::to_value(&resource_test_result.actual_state)?);
561-
get_metadata_from_result(&mut resource_test_result.actual_state, &mut metadata)?;
571+
get_metadata_from_result(Some(&mut self.context), &mut resource_test_result.actual_state, &mut metadata)?;
562572
},
563573
TestResult::Group(group) => {
564574
let mut results = Vec::<Value>::new();
@@ -765,6 +775,11 @@ impl Configurator {
765775
end_datetime: Some(end_datetime.to_rfc3339()),
766776
duration: Some(end_datetime.signed_duration_since(self.context.start_datetime).to_string()),
767777
security_context: Some(self.context.security_context.clone()),
778+
restart_required: if let Some(restart_required) = self.context.restart_required.clone() {
779+
Some(restart_required)
780+
} else {
781+
None
782+
},
768783
}
769784
),
770785
other: Map::new(),

0 commit comments

Comments
 (0)