Skip to content

Commit eb108b1

Browse files
SteveL-MSFTSteve Lee (POWERSHELL HE/HIM) (from Dev Box)
authored andcommitted
Add support for using get if resource doesn't implement export
1 parent c1f2eb9 commit eb108b1

File tree

6 files changed

+147
-1
lines changed

6 files changed

+147
-1
lines changed

dsc/tests/dsc_export.tests.ps1

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,4 +181,42 @@ resources:
181181
$out.resources[0].properties.psobject.properties.name | Should -Not -Contain '_securityContext'
182182
$out.resources[0].properties.psobject.properties.name | Should -Not -Contain '_name'
183183
}
184+
185+
It 'Export can be used with a resource that only implements Get with filter' {
186+
$yaml = @'
187+
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
188+
resources:
189+
- name: NoExport
190+
type: Test/Get
191+
properties:
192+
name: two
193+
'@
194+
$out = dsc config export -i $yaml | ConvertFrom-Json
195+
$LASTEXITCODE | Should -Be 0
196+
$out.resources.count | Should -Be 1
197+
$out.resources[0].type | Should -BeExactly 'Test/Get'
198+
$out.resources[0].properties.name | Should -BeExactly 'two'
199+
$out.resources[0].properties.id | Should -Be 2
200+
}
201+
202+
It 'Export can be used with a resource that only implements Get with no filter' {
203+
$yaml = @'
204+
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
205+
resources:
206+
- name: OS
207+
type: Microsoft/OSInfo
208+
'@
209+
$out = dsc config export -i $yaml | ConvertFrom-Json
210+
$LASTEXITCODE | Should -Be 0
211+
$out.resources.count | Should -Be 1
212+
$out.resources[0].type | Should -BeExactly 'Microsoft/OSInfo'
213+
$expectedOs = if ($IsWindows) {
214+
'Windows'
215+
} elseif ($IsMacOS) {
216+
'macOS'
217+
} else {
218+
'Linux'
219+
}
220+
$out.resources[0].properties.family | Should -BeExactly $expectedOs
221+
}
184222
}

dsc_lib/src/dscresources/command_resource.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,26 @@ pub fn get_schema(resource: &ResourceManifest, cwd: &str) -> Result<String, DscE
487487
/// Error returned if the resource does not successfully export the current state
488488
pub fn invoke_export(resource: &ResourceManifest, cwd: &str, input: Option<&str>) -> Result<ExportResult, DscError> {
489489
let Some(export) = resource.export.as_ref() else {
490+
// see if get is supported and use that instead
491+
if resource.get.is_some() {
492+
info!("{}", t!("dscresources.commandResource.exportNotSupportedUsingGet", resource = &resource.resource_type));
493+
let get_result = invoke_get(resource, cwd, input.unwrap_or(""))?;
494+
let mut instances: Vec<Value> = Vec::new();
495+
match get_result {
496+
GetResult::Group(group_response) => {
497+
for result in group_response {
498+
instances.push(serde_json::to_value(result)?);
499+
}
500+
},
501+
GetResult::Resource(response) => {
502+
instances.push(response.actual_state);
503+
}
504+
}
505+
return Ok(ExportResult {
506+
actual_state: instances,
507+
});
508+
}
509+
// if neither export nor get is supported, return an error
490510
return Err(DscError::Operation(t!("dscresources.commandResource.exportNotSupported", resource = &resource.resource_type).to_string()))
491511
};
492512

@@ -840,7 +860,7 @@ fn json_to_hashmap(json: &str) -> Result<HashMap<String, String>, DscError> {
840860
},
841861
Value::Null => {
842862
// ignore null values
843-
},
863+
},
844864
Value::Object(_) => {
845865
return Err(DscError::Operation(t!("dscresources.commandResource.invalidKey", key = key).to_string()));
846866
},
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
{
2+
"$schema": "https://aka.ms/dsc/schemas/v3/bundled/resource/manifest.json",
3+
"type": "Test/Get",
4+
"version": "0.1.0",
5+
"get": {
6+
"executable": "dsctest",
7+
"args": [
8+
"get",
9+
{
10+
"jsonInputArg": "--input",
11+
"mandatory": true
12+
}
13+
]
14+
},
15+
"schema": {
16+
"command": {
17+
"executable": "dsctest",
18+
"args": [
19+
"schema",
20+
"-s",
21+
"get"
22+
]
23+
}
24+
}
25+
}

tools/dsctest/src/args.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ pub enum Schemas {
1010
ExitCode,
1111
Export,
1212
Exporter,
13+
Get,
1314
InDesiredState,
1415
Metadata,
1516
Sleep,
@@ -57,6 +58,12 @@ pub enum SubCommand {
5758
input: String,
5859
},
5960

61+
#[clap(name = "get", about = "Get a resource")]
62+
Get {
63+
#[clap(name = "input", short, long, help = "The input to the get command as JSON")]
64+
input: String,
65+
},
66+
6067
#[clap(name = "in-desired-state", about = "Specify if the resource is in the desired state")]
6168
InDesiredState {
6269
#[clap(name = "input", short, long, help = "The input to the in desired state command as JSON")]

tools/dsctest/src/get.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
use schemars::JsonSchema;
5+
use serde::{Deserialize, Serialize};
6+
7+
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
8+
#[serde(deny_unknown_fields)]
9+
pub struct Get {
10+
pub name: Option<String>,
11+
pub id: Option<i32>,
12+
}

tools/dsctest/src/main.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ mod exist;
77
mod exit_code;
88
mod export;
99
mod exporter;
10+
mod get;
1011
mod in_desired_state;
1112
mod metadata;
1213
mod sleep;
@@ -22,6 +23,7 @@ use crate::exist::{Exist, State};
2223
use crate::exit_code::ExitCode;
2324
use crate::export::Export;
2425
use crate::exporter::{Exporter, Resource};
26+
use crate::get::Get;
2527
use crate::in_desired_state::InDesiredState;
2628
use crate::metadata::Metadata;
2729
use crate::sleep::Sleep;
@@ -113,6 +115,45 @@ fn main() {
113115
}
114116
String::new()
115117
},
118+
SubCommand::Get { input } => {
119+
let get = match serde_json::from_str::<Get>(&input) {
120+
Ok(get) => get,
121+
Err(err) => {
122+
eprintln!("Error JSON does not match schema: {err}");
123+
std::process::exit(1);
124+
}
125+
};
126+
let instances = vec![
127+
Get {
128+
name : Some("one".to_string()),
129+
id: Some(1),
130+
},
131+
Get {
132+
name : Some("two".to_string()),
133+
id: Some(2),
134+
},
135+
Get {
136+
name : Some("three".to_string()),
137+
id: Some(3),
138+
},
139+
];
140+
// depending on the input, return the appropriate instance whether it is name or id or both
141+
let resource = if let Some(name) = get.name {
142+
instances.into_iter().find(|i| i.name == Some(name.clone())).unwrap_or_else(|| {
143+
eprintln!("No instance found with name: {}", name);
144+
std::process::exit(1);
145+
})
146+
} else if let Some(id) = get.id {
147+
instances.into_iter().find(|i| i.id == Some(id)).unwrap_or_else(|| {
148+
eprintln!("No instance found with id: {}", id);
149+
std::process::exit(1);
150+
})
151+
} else {
152+
eprintln!("No name or id provided in input");
153+
std::process::exit(1);
154+
};
155+
serde_json::to_string(&resource).unwrap()
156+
},
116157
SubCommand::InDesiredState { input } => {
117158
let mut in_desired_state = match serde_json::from_str::<in_desired_state::InDesiredState>(&input) {
118159
Ok(in_desired_state) => in_desired_state,
@@ -162,6 +203,9 @@ fn main() {
162203
Schemas::Exporter => {
163204
schema_for!(Exporter)
164205
},
206+
Schemas::Get => {
207+
schema_for!(Get)
208+
},
165209
Schemas::InDesiredState => {
166210
schema_for!(InDesiredState)
167211
},

0 commit comments

Comments
 (0)