Skip to content

Commit 012cf3d

Browse files
authored
Merge pull request #975 from SteveL-MSFT/restart-required
Enable resources to return _restartRequired metadata
2 parents 5affd98 + ce9d542 commit 012cf3d

File tree

7 files changed

+118
-10
lines changed

7 files changed

+118
-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: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,4 +71,73 @@ 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+
}
126+
127+
It 'invalid item in _restartRequired metadata is a warning' {
128+
$configYaml = @'
129+
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
130+
resources:
131+
- name: test
132+
type: Test/Metadata
133+
properties:
134+
_metadata:
135+
_restartRequired:
136+
- invalid: item
137+
'@
138+
$out = dsc config get -i $configYaml 2>$TestDrive/error.log | ConvertFrom-Json
139+
$LASTEXITCODE | Should -Be 0
140+
(Get-Content $TestDrive/error.log) | Should -BeLike "*WARN*Resource returned '_metadata' property '_restartRequired' which contains invalid value: ``[{`"invalid`":`"item`"}]*"
141+
$out.results[0].metadata._restartRequired | Should -BeNullOrEmpty
142+
}
74143
}

dsc_lib/locales/en-us.toml

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

7475
[discovery.commandDiscovery]
7576
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,
@@ -18,6 +18,7 @@ pub struct Context {
1818
pub security_context: SecurityContextKind,
1919
pub variables: Map<String, Value>,
2020
pub start_datetime: DateTime<Local>,
21+
pub restart_required: Option<Vec<RestartRequired>>,
2122
}
2223

2324
impl Context {
@@ -35,6 +36,7 @@ impl Context {
3536
},
3637
variables: Map::new(),
3738
start_datetime: chrono::Local::now(),
39+
restart_required: None,
3840
}
3941
}
4042
}

dsc_lib/src/configure/mod.rs

Lines changed: 19 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 Ok(restart_required) = serde_json::from_value::<Vec<RestartRequired>>(value.clone()) {
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 {
@@ -342,7 +352,7 @@ impl Configurator {
342352
match &mut get_result {
343353
GetResult::Resource(ref mut resource_result) => {
344354
self.context.references.insert(format!("{}:{}", resource.resource_type, resource.name), serde_json::to_value(&resource_result.actual_state)?);
345-
get_metadata_from_result(&mut resource_result.actual_state, &mut metadata)?;
355+
get_metadata_from_result(Some(&mut self.context), &mut resource_result.actual_state, &mut metadata)?;
346356
},
347357
GetResult::Group(group) => {
348358
let mut results = Vec::<Value>::new();
@@ -499,7 +509,7 @@ impl Configurator {
499509
match &mut set_result {
500510
SetResult::Resource(resource_result) => {
501511
self.context.references.insert(format!("{}:{}", resource.resource_type, resource.name), serde_json::to_value(&resource_result.after_state)?);
502-
get_metadata_from_result(&mut resource_result.after_state, &mut metadata)?;
512+
get_metadata_from_result(Some(&mut self.context), &mut resource_result.after_state, &mut metadata)?;
503513
},
504514
SetResult::Group(group) => {
505515
let mut results = Vec::<Value>::new();
@@ -573,7 +583,7 @@ impl Configurator {
573583
match &mut test_result {
574584
TestResult::Resource(resource_test_result) => {
575585
self.context.references.insert(format!("{}:{}", resource.resource_type, resource.name), serde_json::to_value(&resource_test_result.actual_state)?);
576-
get_metadata_from_result(&mut resource_test_result.actual_state, &mut metadata)?;
586+
get_metadata_from_result(Some(&mut self.context), &mut resource_test_result.actual_state, &mut metadata)?;
577587
},
578588
TestResult::Group(group) => {
579589
let mut results = Vec::<Value>::new();
@@ -796,6 +806,7 @@ impl Configurator {
796806
end_datetime: Some(end_datetime.to_rfc3339()),
797807
duration: Some(end_datetime.signed_duration_since(self.context.start_datetime).to_string()),
798808
security_context: Some(self.context.security_context.clone()),
809+
restart_required: self.context.restart_required.clone(),
799810
}
800811
),
801812
other: Map::new(),

0 commit comments

Comments
 (0)