Skip to content

Add support for using get if resource doesn't implement export #1035

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions dsc/tests/dsc_export.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -181,4 +181,42 @@ resources:
$out.resources[0].properties.psobject.properties.name | Should -Not -Contain '_securityContext'
$out.resources[0].properties.psobject.properties.name | Should -Not -Contain '_name'
}

It 'Export can be used with a resource that only implements Get with filter' {
$yaml = @'
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: NoExport
type: Test/Get
properties:
name: two
'@
$out = dsc config export -i $yaml | ConvertFrom-Json
$LASTEXITCODE | Should -Be 0
$out.resources.count | Should -Be 1
$out.resources[0].type | Should -BeExactly 'Test/Get'
$out.resources[0].properties.name | Should -BeExactly 'two'
$out.resources[0].properties.id | Should -Be 2
}

It 'Export can be used with a resource that only implements Get with no filter' {
$yaml = @'
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
resources:
- name: OS
type: Microsoft/OSInfo
'@
$out = dsc config export -i $yaml | ConvertFrom-Json
$LASTEXITCODE | Should -Be 0
$out.resources.count | Should -Be 1
$out.resources[0].type | Should -BeExactly 'Microsoft/OSInfo'
$expectedOs = if ($IsWindows) {
'Windows'
} elseif ($IsMacOS) {
'macOS'
} else {
'Linux'
}
$out.resources[0].properties.family | Should -BeExactly $expectedOs
}
}
22 changes: 21 additions & 1 deletion dsc_lib/src/dscresources/command_resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,26 @@ pub fn get_schema(resource: &ResourceManifest, cwd: &str) -> Result<String, DscE
/// Error returned if the resource does not successfully export the current state
pub fn invoke_export(resource: &ResourceManifest, cwd: &str, input: Option<&str>) -> Result<ExportResult, DscError> {
let Some(export) = resource.export.as_ref() else {
// see if get is supported and use that instead
if resource.get.is_some() {
info!("{}", t!("dscresources.commandResource.exportNotSupportedUsingGet", resource = &resource.resource_type));
let get_result = invoke_get(resource, cwd, input.unwrap_or(""))?;
let mut instances: Vec<Value> = Vec::new();
match get_result {
GetResult::Group(group_response) => {
for result in group_response {
instances.push(serde_json::to_value(result)?);
}
},
GetResult::Resource(response) => {
instances.push(response.actual_state);
}
}
return Ok(ExportResult {
actual_state: instances,
});
}
// if neither export nor get is supported, return an error
return Err(DscError::Operation(t!("dscresources.commandResource.exportNotSupported", resource = &resource.resource_type).to_string()))
};

Expand Down Expand Up @@ -840,7 +860,7 @@ fn json_to_hashmap(json: &str) -> Result<HashMap<String, String>, DscError> {
},
Value::Null => {
// ignore null values
},
},
Value::Object(_) => {
return Err(DscError::Operation(t!("dscresources.commandResource.invalidKey", key = key).to_string()));
},
Expand Down
3 changes: 0 additions & 3 deletions osinfo/osinfo.dsc.resource.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,6 @@
"get": {
"executable": "osinfo"
},
"export": {
"executable": "osinfo"
},
"schema": {
"embedded": {
"$schema": "http://json-schema.org/draft-07/schema#",
Expand Down
25 changes: 25 additions & 0 deletions tools/dsctest/dscget.dsc.resource.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"$schema": "https://aka.ms/dsc/schemas/v3/bundled/resource/manifest.json",
"type": "Test/Get",
"version": "0.1.0",
"get": {
"executable": "dsctest",
"args": [
"get",
{
"jsonInputArg": "--input",
"mandatory": true
}
]
},
"schema": {
"command": {
"executable": "dsctest",
"args": [
"schema",
"-s",
"get"
]
}
}
}
7 changes: 7 additions & 0 deletions tools/dsctest/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub enum Schemas {
ExitCode,
Export,
Exporter,
Get,
InDesiredState,
Metadata,
Sleep,
Expand Down Expand Up @@ -57,6 +58,12 @@ pub enum SubCommand {
input: String,
},

#[clap(name = "get", about = "Get a resource")]
Get {
#[clap(name = "input", short, long, help = "The input to the get command as JSON")]
input: String,
},

#[clap(name = "in-desired-state", about = "Specify if the resource is in the desired state")]
InDesiredState {
#[clap(name = "input", short, long, help = "The input to the in desired state command as JSON")]
Expand Down
12 changes: 12 additions & 0 deletions tools/dsctest/src/get.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct Get {
pub name: Option<String>,
pub id: Option<i32>,
}
44 changes: 44 additions & 0 deletions tools/dsctest/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ mod exist;
mod exit_code;
mod export;
mod exporter;
mod get;
mod in_desired_state;
mod metadata;
mod sleep;
Expand All @@ -22,6 +23,7 @@ use crate::exist::{Exist, State};
use crate::exit_code::ExitCode;
use crate::export::Export;
use crate::exporter::{Exporter, Resource};
use crate::get::Get;
use crate::in_desired_state::InDesiredState;
use crate::metadata::Metadata;
use crate::sleep::Sleep;
Expand Down Expand Up @@ -113,6 +115,45 @@ fn main() {
}
String::new()
},
SubCommand::Get { input } => {
let get = match serde_json::from_str::<Get>(&input) {
Ok(get) => get,
Err(err) => {
eprintln!("Error JSON does not match schema: {err}");
std::process::exit(1);
}
};
let instances = vec![
Get {
name : Some("one".to_string()),
id: Some(1),
},
Get {
name : Some("two".to_string()),
id: Some(2),
},
Get {
name : Some("three".to_string()),
id: Some(3),
},
];
// depending on the input, return the appropriate instance whether it is name or id or both
let resource = if let Some(name) = get.name {
instances.into_iter().find(|i| i.name == Some(name.clone())).unwrap_or_else(|| {
eprintln!("No instance found with name: {}", name);
std::process::exit(1);
})
} else if let Some(id) = get.id {
instances.into_iter().find(|i| i.id == Some(id)).unwrap_or_else(|| {
eprintln!("No instance found with id: {}", id);
std::process::exit(1);
})
} else {
eprintln!("No name or id provided in input");
std::process::exit(1);
};
serde_json::to_string(&resource).unwrap()
},
SubCommand::InDesiredState { input } => {
let mut in_desired_state = match serde_json::from_str::<in_desired_state::InDesiredState>(&input) {
Ok(in_desired_state) => in_desired_state,
Expand Down Expand Up @@ -162,6 +203,9 @@ fn main() {
Schemas::Exporter => {
schema_for!(Exporter)
},
Schemas::Get => {
schema_for!(Get)
},
Schemas::InDesiredState => {
schema_for!(InDesiredState)
},
Expand Down
Loading