Skip to content

Commit 5e860e2

Browse files
authored
Merge pull request #682 from SteveL-MSFT/exporter
Add `exporter` kind of resource
2 parents 1080ca7 + 57adf5f commit 5e860e2

File tree

8 files changed

+122
-10
lines changed

8 files changed

+122
-10
lines changed

dsc/tests/dsc_args.tests.ps1

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ actualState:
9797
$resource = $obj | y2j | ConvertFrom-Json
9898
$resource | Should -Not -BeNullOrEmpty
9999
$resource.Type | Should -BeLike '*/*'
100-
$resource.Kind | Should -BeIn ('resource', 'group', 'importer', 'adapter')
100+
$resource.Kind | Should -BeIn ('resource', 'group', 'exporter', 'importer', 'adapter')
101101
}
102102
}
103103

dsc/tests/dsc_export.tests.ps1

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,4 +135,30 @@ Describe 'resource export tests' {
135135
$out.metadata.hello | Should -BeExactly 'world'
136136
$out.metadata.'Microsoft.DSC'.operation | Should -BeExactly 'export'
137137
}
138+
139+
It 'Works with Exporter resource' {
140+
$yaml = @'
141+
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json
142+
resources:
143+
- name: export this
144+
type: Test/Exporter
145+
properties:
146+
typeNames:
147+
- Test/Foo
148+
- Test/Bar
149+
'@
150+
$out = dsc config export -i $yaml | ConvertFrom-Json
151+
$LASTEXITCODE | Should -Be 0
152+
$out.resources | Should -HaveCount 2
153+
$out.resources[0].type | Should -BeExactly 'Test/Foo'
154+
$out.resources[0].name | Should -BeExactly 'test'
155+
$out.resources[0].properties.psobject.properties | Should -HaveCount 2
156+
$out.resources[0].properties.foo | Should -BeExactly 'bar'
157+
$out.resources[0].properties.hello | Should -BeExactly 'world'
158+
$out.resources[1].type | Should -BeExactly 'Test/Bar'
159+
$out.resources[1].name | Should -BeExactly 'test'
160+
$out.resources[1].properties.psobject.properties | Should -HaveCount 2
161+
$out.resources[1].properties.foo | Should -BeExactly 'bar'
162+
$out.resources[1].properties.hello | Should -BeExactly 'world'
163+
}
138164
}

dsc_lib/src/configure/mod.rs

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4-
use crate::configure::config_doc::{ExecutionKind, Metadata};
4+
use crate::configure::config_doc::{ExecutionKind, Metadata, Resource};
55
use crate::configure::parameters::Input;
66
use crate::dscerror::DscError;
77
use crate::dscresources::invoke_result::ExportResult;
@@ -62,14 +62,21 @@ pub fn add_resource_export_results_to_configuration(resource: &DscResource, adap
6262
_ => resource.export(input)?
6363
};
6464

65-
for (i, instance) in export_result.actual_state.iter().enumerate() {
66-
let mut r = config_doc::Resource::new();
67-
r.resource_type.clone_from(&resource.type_name);
68-
r.name = format!("{}-{i}", r.resource_type);
69-
let props: Map<String, Value> = serde_json::from_value(instance.clone())?;
70-
r.properties = escape_property_values(&props)?;
71-
72-
conf.resources.push(r);
65+
if resource.kind == Kind::Exporter {
66+
for instance in &export_result.actual_state {
67+
let resource = serde_json::from_value::<Resource>(instance.clone())?;
68+
conf.resources.push(resource);
69+
}
70+
} else {
71+
for (i, instance) in export_result.actual_state.iter().enumerate() {
72+
let mut r = config_doc::Resource::new();
73+
r.resource_type.clone_from(&resource.type_name);
74+
r.name = format!("{}-{i}", r.resource_type);
75+
let props: Map<String, Value> = serde_json::from_value(instance.clone())?;
76+
r.properties = escape_property_values(&props)?;
77+
78+
conf.resources.push(r);
79+
}
7380
}
7481

7582
Ok(export_result)

dsc_lib/src/dscresources/resource_manifest.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ use crate::{dscerror::DscError, schemas::DscRepoSchema};
1414
#[serde(rename_all = "camelCase")]
1515
pub enum Kind {
1616
Adapter,
17+
Exporter,
1718
Group,
1819
Importer,
1920
Resource,
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"$schema": "https://aka.ms/dsc/schemas/v3/bundled/resource/manifest.json",
3+
"type": "Test/Exporter",
4+
"version": "0.1.0",
5+
"kind": "exporter",
6+
"export": {
7+
"executable": "dsctest",
8+
"args": [
9+
"exporter",
10+
{
11+
"jsonInputArg": "--input",
12+
"mandatory": true
13+
}
14+
]
15+
},
16+
"schema": {
17+
"command": {
18+
"executable": "dsctest",
19+
"args": [
20+
"schema",
21+
"-s",
22+
"exporter"
23+
]
24+
}
25+
}
26+
}

tools/dsctest/src/args.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ pub enum Schemas {
1010
ExitCode,
1111
InDesiredState,
1212
Export,
13+
Exporter,
1314
Sleep,
1415
Trace,
1516
WhatIf,
@@ -53,6 +54,11 @@ pub enum SubCommand {
5354
#[clap(name = "input", short, long, help = "The input to the export command as JSON")]
5455
input: String,
5556
},
57+
#[clap(name = "exporter", about = "Exports different types of resources")]
58+
Exporter {
59+
#[clap(name = "input", short, long, help = "The input to the exporter command as JSON")]
60+
input: String,
61+
},
5662

5763
#[clap(name = "schema", about = "Get the JSON schema for a subcommand")]
5864
Schema {

tools/dsctest/src/exporter.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
use schemars::JsonSchema;
5+
use serde::{Deserialize, Serialize};
6+
use serde_json::{Map, Value};
7+
8+
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
9+
pub struct Resource {
10+
pub name: String,
11+
pub r#type: String,
12+
pub properties: Map<String, Value>,
13+
}
14+
15+
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
16+
#[serde(deny_unknown_fields)]
17+
pub struct Exporter {
18+
#[serde(rename = "typeNames")]
19+
pub type_names: Vec<String>,
20+
}

tools/dsctest/src/main.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,21 @@ mod exist;
77
mod exit_code;
88
mod in_desired_state;
99
mod export;
10+
mod exporter;
1011
mod sleep;
1112
mod trace;
1213
mod whatif;
1314

1415
use args::{Args, Schemas, SubCommand};
1516
use clap::Parser;
1617
use schemars::schema_for;
18+
use serde_json::Map;
1719
use crate::delete::Delete;
1820
use crate::exist::{Exist, State};
1921
use crate::exit_code::ExitCode;
2022
use crate::in_desired_state::InDesiredState;
2123
use crate::export::Export;
24+
use crate::exporter::{Exporter, Resource};
2225
use crate::sleep::Sleep;
2326
use crate::trace::Trace;
2427
use crate::whatif::WhatIf;
@@ -97,6 +100,26 @@ fn main() {
97100
}
98101
String::new()
99102
},
103+
SubCommand::Exporter { input } => {
104+
let exporter = match serde_json::from_str::<Exporter>(&input) {
105+
Ok(exporter) => exporter,
106+
Err(err) => {
107+
eprintln!("Error JSON does not match schema: {err}");
108+
std::process::exit(1);
109+
}
110+
};
111+
for type_name in exporter.type_names {
112+
let mut resource = Resource {
113+
name: "test".to_string(),
114+
r#type: type_name,
115+
properties: Map::new(),
116+
};
117+
resource.properties.insert("foo".to_string(), serde_json::Value::String("bar".to_string()));
118+
resource.properties.insert("hello".to_string(), serde_json::Value::String("world".to_string()));
119+
println!("{}", serde_json::to_string(&resource).unwrap());
120+
}
121+
String::new()
122+
},
100123
SubCommand::Schema { subcommand } => {
101124
let schema = match subcommand {
102125
Schemas::Delete => {
@@ -114,6 +137,9 @@ fn main() {
114137
Schemas::Export => {
115138
schema_for!(Export)
116139
},
140+
Schemas::Exporter => {
141+
schema_for!(Exporter)
142+
},
117143
Schemas::Sleep => {
118144
schema_for!(Sleep)
119145
},

0 commit comments

Comments
 (0)