Skip to content

Commit

Permalink
Merge pull request #464 from SteveL-MSFT/include-set-test
Browse files Browse the repository at this point in the history
Enable `set` and `test` for Import resources
  • Loading branch information
SteveL-MSFT authored Jun 18, 2024
2 parents 18b913b + a706d91 commit f0ffdf4
Show file tree
Hide file tree
Showing 8 changed files with 110 additions and 106 deletions.
4 changes: 4 additions & 0 deletions dsc/examples/osinfo_parameters.dsc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,7 @@ resources:
type: Microsoft/OSInfo
properties:
family: "[parameters('osFamily')]"
- name: another os instance
type: Microsoft/OSInfo
properties:
family: macOS
38 changes: 4 additions & 34 deletions dsc/src/subcommand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ use crate::tablewriter::Table;
use crate::util::{DSC_CONFIG_ROOT, EXIT_DSC_ERROR, EXIT_INVALID_INPUT, EXIT_JSON_ERROR, EXIT_VALIDATION_FAILED, get_schema, write_output, get_input, set_dscconfigroot, validate_json};
use dsc_lib::configure::{Configurator, config_doc::ExecutionKind, config_result::ResourceGetResult};
use dsc_lib::dscerror::DscError;
use dsc_lib::dscresources::invoke_result::{
GroupResourceSetResponse, GroupResourceTestResponse, ResolveResult, TestResult
};
use dsc_lib::dscresources::invoke_result::ResolveResult;
use dsc_lib::{
DscManager,
dscresources::invoke_result::ValidateResult,
Expand All @@ -27,11 +25,7 @@ pub fn config_get(configurator: &mut Configurator, format: &Option<OutputFormat>
match configurator.invoke_get() {
Ok(result) => {
if *as_group {
let mut group_result = Vec::<ResourceGetResult>::new();
for result in result.results {
group_result.push(result);
};
let json = match serde_json::to_string(&group_result) {
let json = match serde_json::to_string(&(result.results)) {
Ok(json) => json,
Err(err) => {
error!("JSON Error: {err}");
Expand Down Expand Up @@ -66,10 +60,7 @@ pub fn config_set(configurator: &mut Configurator, format: &Option<OutputFormat>
match configurator.invoke_set(false) {
Ok(result) => {
if *as_group {
let group_result = GroupResourceSetResponse {
results: result.results
};
let json = match serde_json::to_string(&group_result) {
let json = match serde_json::to_string(&(result.results)) {
Ok(json) => json,
Err(err) => {
error!("JSON Error: {err}");
Expand Down Expand Up @@ -104,23 +95,6 @@ pub fn config_test(configurator: &mut Configurator, format: &Option<OutputFormat
match configurator.invoke_test() {
Ok(result) => {
if *as_group {
let mut in_desired_state = true;
for test_result in &result.results {
match &test_result.result {
TestResult::Resource(resource_test_result) => {
if !resource_test_result.in_desired_state {
in_desired_state = false;
break;
}
},
TestResult::Group(group_resource_test_result) => {
if !group_resource_test_result.in_desired_state {
in_desired_state = false;
break;
}
}
}
}
let json = if *as_get {
let mut group_result = Vec::<ResourceGetResult>::new();
for test_result in result.results {
Expand All @@ -135,11 +109,7 @@ pub fn config_test(configurator: &mut Configurator, format: &Option<OutputFormat
}
}
else {
let group_result = GroupResourceTestResponse {
results: result.results,
in_desired_state
};
match serde_json::to_string(&group_result) {
match serde_json::to_string(&(result.results)) {
Ok(json) => json,
Err(err) => {
error!("JSON Error: {err}");
Expand Down
43 changes: 34 additions & 9 deletions dsc/tests/dsc_include.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,6 @@ Describe 'Include tests' {
$osinfoParametersConfigPath = Get-Item (Join-Path $includePath 'osinfo.parameters.yaml')

$logPath = Join-Path $TestDrive 'stderr.log'

$includeConfig = @'
`$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json
resources:
- name: Echo
type: Test/Echo
properties:
output: Hello World
'@
}

It 'Include config with default parameters' {
Expand Down Expand Up @@ -183,4 +174,38 @@ resources:
$out.results[1].result[0].result[0].type | Should -Be 'Test/Echo'
$out.results[1].result[0].result[0].result[0].actualState.output | Should -Be 'one'
}

It 'Set with include works' {
$echoConfig = @'
$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json
resources:
- name: one
type: Test/Echo
properties:
output: Hello World
'@

$echoConfigPath = Join-Path $TestDrive 'echo.dsc.yaml'
$echoConfig | Set-Content -Path $echoConfigPath -Encoding utf8
# need to escape backslashes for YAML
$echoConfigPathParent = (Split-Path $echoConfigPath -Parent).Replace('\', '\\')
$echoConfigPathLeaf = (Split-Path $echoConfigPath -Leaf).Replace('\', '\\')
$directorySeparator = [System.IO.Path]::DirectorySeparatorChar.ToString().Replace('\', '\\')

$includeConfig = @"
`$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2024/04/config/document.json
resources:
- name: nested
type: Microsoft.DSC/Include
properties:
configurationFile: "[concat('$echoConfigPathParent', '$directorySeparator', '$echoConfigPathLeaf')]"
"@

$out = dsc config set -d $includeConfig | ConvertFrom-Json
$LASTEXITCODE | Should -Be 0
$out.results[0].result[0].name | Should -Be 'one'
$out.results[0].result[0].type | Should -Be 'Test/Echo'
$out.results[0].result[0].result.afterState.output | Should -Be 'Hello World'
$out.hadErrors | Should -Be $false
}
}
9 changes: 5 additions & 4 deletions dsc_lib/src/configure/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
use crate::configure::config_doc::{ExecutionKind, Metadata};
use crate::configure::parameters::Input;
use crate::dscerror::DscError;
use crate::dscresources::dscresource::get_diff;
use crate::dscresources::invoke_result::GetResult;
use crate::dscresources::{dscresource::{Capability, Invoke}, invoke_result::{SetResult, ResourceSetResponse}};
use crate::dscresources::resource_manifest::Kind;
use crate::dscresources::{
{dscresource::{Capability, Invoke, get_diff}, invoke_result::{SetResult, ResourceSetResponse}},
invoke_result::GetResult,
resource_manifest::Kind,
};
use crate::DscResource;
use crate::discovery::Discovery;
use crate::parser::Statement;
Expand Down
3 changes: 3 additions & 0 deletions dsc_lib/src/dscerror.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ pub enum DscError {
#[error("Resource not found: {0}")]
ResourceNotFound(String),

#[error("Resource manifest not found: {0}")]
ResourceManifestNotFound(String),

#[error("Schema: {0}")]
Schema(String),

Expand Down
26 changes: 19 additions & 7 deletions dsc_lib/src/dscresources/command_resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use serde_json::Value;
use std::{collections::HashMap, env, io::{Read, Write}, process::{Command, Stdio}};
use crate::{configure::{config_doc::ExecutionKind, {config_result::ResourceGetResult, parameters, Configurator}}, util::parse_input_to_json};
use crate::dscerror::DscError;
use super::{dscresource::get_diff, invoke_result::{ExportResult, GetResult, ResolveResult, SetResult, TestResult, ValidateResult, ResourceGetResponse, ResourceSetResponse, ResourceTestResponse}, resource_manifest::{ArgKind, InputKind, Kind, ResourceManifest, ReturnKind, SchemaKind}};
use super::{dscresource::get_diff, invoke_result::{ExportResult, GetResult, ResolveResult, SetResult, TestResult, ValidateResult, ResourceGetResponse, ResourceSetResponse, ResourceTestResponse, get_in_desired_state}, resource_manifest::{ArgKind, InputKind, Kind, ResourceManifest, ReturnKind, SchemaKind}};
use tracing::{error, warn, info, debug, trace};

pub const EXIT_PROCESS_TERMINATED: i32 = 0x102;
Expand Down Expand Up @@ -94,7 +94,13 @@ pub fn invoke_get(resource: &ResourceManifest, cwd: &str, filter: &str) -> Resul
/// Error returned if the resource does not successfully set the desired state
#[allow(clippy::too_many_lines)]
pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_test: bool, execution_type: &ExecutionKind) -> Result<SetResult, DscError> {
// TODO: support import resources
debug!("Invoking set for '{}'", &resource.resource_type);
if resource.kind == Some(Kind::Import) {
let mut configurator = get_configurator(resource, cwd, desired)?;
let config_result = configurator.invoke_set(skip_test)?;
return Ok(SetResult::Group(config_result.results));
}

let operation_type: String;
let mut is_synthetic_what_if = false;
let set_method = match execution_type {
Expand Down Expand Up @@ -124,16 +130,17 @@ pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_te
if is_synthetic_what_if {
return Ok(test_result.into());
}
let (in_desired_state, actual_state) = match test_result {
let (in_desired_state, actual_state) = match &test_result {
TestResult::Group(group_response) => {
let in_desired_state = get_in_desired_state(&test_result);
let mut result_array: Vec<Value> = Vec::new();
for result in group_response.results {
for result in group_response {
result_array.push(serde_json::to_value(result)?);
}
(group_response.in_desired_state, Value::from(result_array))
(in_desired_state, Value::from(result_array))
},
TestResult::Resource(response) => {
(response.in_desired_state, response.actual_state)
(response.in_desired_state, response.actual_state.clone())
}
};

Expand Down Expand Up @@ -267,7 +274,12 @@ pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_te
///
/// Error is returned if the underlying command returns a non-zero exit code.
pub fn invoke_test(resource: &ResourceManifest, cwd: &str, expected: &str) -> Result<TestResult, DscError> {
// TODO: support import resources
debug!("Invoking test for '{}'", &resource.resource_type);
if resource.kind == Some(Kind::Import) {
let mut configurator = get_configurator(resource, cwd, expected)?;
let config_result = configurator.invoke_test()?;
return Ok(TestResult::Group(config_result.results));
}

let Some(test) = &resource.test else {
info!("Resource '{}' does not implement test, performing synthetic test", &resource.resource_type);
Expand Down
21 changes: 19 additions & 2 deletions dsc_lib/src/dscresources/dscresource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use std::collections::HashMap;
use tracing::debug;

use super::{command_resource, dscerror, invoke_result::{ExportResult, GetResult, ResolveResult, ResourceTestResponse, SetResult, TestResult, ValidateResult}, resource_manifest::import_manifest};

Expand Down Expand Up @@ -187,6 +188,7 @@ pub trait Invoke {

impl Invoke for DscResource {
fn get(&self, filter: &str) -> Result<GetResult, DscError> {
debug!("Invoking get for resource: {}", self.type_name);
match &self.implemented_as {
ImplementedAs::Custom(_custom) => {
Err(DscError::NotImplemented("get custom resources".to_string()))
Expand All @@ -202,6 +204,7 @@ impl Invoke for DscResource {
}

fn set(&self, desired: &str, skip_test: bool, execution_type: &ExecutionKind) -> Result<SetResult, DscError> {
debug!("Invoking set for resource: {}", self.type_name);
match &self.implemented_as {
ImplementedAs::Custom(_custom) => {
Err(DscError::NotImplemented("set custom resources".to_string()))
Expand All @@ -217,6 +220,7 @@ impl Invoke for DscResource {
}

fn test(&self, expected: &str) -> Result<TestResult, DscError> {
debug!("Invoking test for resource: {}", self.type_name);
match &self.implemented_as {
ImplementedAs::Custom(_custom) => {
Err(DscError::NotImplemented("test custom resources".to_string()))
Expand All @@ -230,7 +234,15 @@ impl Invoke for DscResource {
let resource_manifest = import_manifest(manifest.clone())?;
if resource_manifest.test.is_none() {
let get_result = self.get(expected)?;
let desired_state = serde_json::from_str(expected)?;
let desired_state = if self.kind == Kind::Import {
let config = self.resolve(expected)?.configuration;
// TODO: implement way to resolve entire config doc including expressions and parameters
// as the raw configuration (desired state) won't match the result, also convert the desired
// state to a TestResult so the comparison is consistent
serde_json::to_value(config["resources"].clone())?
} else {
serde_json::from_str(expected)?
};
let actual_state = match get_result {
GetResult::Group(results) => {
let mut result_array: Vec<Value> = Vec::new();
Expand All @@ -245,7 +257,7 @@ impl Invoke for DscResource {
};
let diff_properties = get_diff( &desired_state, &actual_state);
let test_result = TestResult::Resource(ResourceTestResponse {
desired_state: serde_json::from_str(expected)?,
desired_state,
actual_state,
in_desired_state: diff_properties.is_empty(),
diff_properties,
Expand All @@ -260,6 +272,7 @@ impl Invoke for DscResource {
}

fn delete(&self, filter: &str) -> Result<(), DscError> {
debug!("Invoking delete for resource: {}", self.type_name);
match &self.implemented_as {
ImplementedAs::Custom(_custom) => {
Err(DscError::NotImplemented("set custom resources".to_string()))
Expand All @@ -275,6 +288,7 @@ impl Invoke for DscResource {
}

fn validate(&self, config: &str) -> Result<ValidateResult, DscError> {
debug!("Invoking validate for resource: {}", self.type_name);
match &self.implemented_as {
ImplementedAs::Custom(_custom) => {
Err(DscError::NotImplemented("validate custom resources".to_string()))
Expand All @@ -290,6 +304,7 @@ impl Invoke for DscResource {
}

fn schema(&self) -> Result<String, DscError> {
debug!("Invoking schema for resource: {}", self.type_name);
match &self.implemented_as {
ImplementedAs::Custom(_custom) => {
Err(DscError::NotImplemented("schema custom resources".to_string()))
Expand All @@ -305,6 +320,7 @@ impl Invoke for DscResource {
}

fn export(&self, input: &str) -> Result<ExportResult, DscError> {
debug!("Invoking export for resource: {}", self.type_name);
let Some(manifest) = &self.manifest else {
return Err(DscError::MissingManifest(self.type_name.clone()));
};
Expand All @@ -313,6 +329,7 @@ impl Invoke for DscResource {
}

fn resolve(&self, input: &str) -> Result<ResolveResult, DscError> {
debug!("Invoking resolve for resource: {}", self.type_name);
let Some(manifest) = &self.manifest else {
return Err(DscError::MissingManifest(self.type_name.clone()));
};
Expand Down
Loading

0 comments on commit f0ffdf4

Please sign in to comment.