diff --git a/dsc/src/subcommand.rs b/dsc/src/subcommand.rs index 25a725b32..2c3bdb52c 100644 --- a/dsc/src/subcommand.rs +++ b/dsc/src/subcommand.rs @@ -802,9 +802,11 @@ pub fn list_resources( let capability_types = [ (Capability::Get, "g"), (Capability::Set, "s"), + (Capability::SetWhatIf, "w"), (Capability::SetHandlesExist, "x"), (Capability::Test, "t"), (Capability::Delete, "d"), + (Capability::DeleteWhatIf, "W"), (Capability::Export, "e"), (Capability::Resolve, "r"), ]; diff --git a/dsc/tests/dsc_resource_list.tests.ps1 b/dsc/tests/dsc_resource_list.tests.ps1 index 267c72ec5..46085f5a2 100644 --- a/dsc/tests/dsc_resource_list.tests.ps1 +++ b/dsc/tests/dsc_resource_list.tests.ps1 @@ -161,4 +161,18 @@ Describe 'Tests for listing resources' { $env:DSC_RESOURCE_PATH = $oldPath } } + + It 'What-if capability is added for resources supporting it for: ' -TestCases @( + @{ resource = 'Test/WhatIf'; capability = 'SetWhatIf' } + @{ resource = 'Test/WhatIfArgKind'; capability = 'SetWhatIf' } + @{ resource = 'Test/WhatIfDelete'; capability = 'DeleteWhatIf' } + @{ resource = 'Test/WhatIfReturnDiff'; capability = 'SetWhatIf' } + ) { + param($resource, $capability) + + $out = dsc resource list $resource | ConvertFrom-Json + $LASTEXITCODE | Should -Be 0 + $out.Count | Should -Be 1 + $out.capabilities | Should -Contain $capability + } } diff --git a/lib/dsc-lib-jsonschema/.versions.json b/lib/dsc-lib-jsonschema/.versions.json index b3c3d0a1a..6ffb355c8 100644 --- a/lib/dsc-lib-jsonschema/.versions.json +++ b/lib/dsc-lib-jsonschema/.versions.json @@ -1,9 +1,11 @@ { "latestMajor": "V3", - "latestMinor": "V3_1", - "latestPatch": "V3_1_3", + "latestMinor": "V3_2", + "latestPatch": "V3_2_0", "all": [ "V3", + "V3_2", + "V3_2_0", "V3_1", "V3_1_3", "V3_1_2", diff --git a/lib/dsc-lib/src/discovery/command_discovery.rs b/lib/dsc-lib/src/discovery/command_discovery.rs index 181613680..4da974bc7 100644 --- a/lib/dsc-lib/src/discovery/command_discovery.rs +++ b/lib/dsc-lib/src/discovery/command_discovery.rs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -use crate::{discovery::{DiscoveryExtensionCache, DiscoveryManifestCache, DiscoveryResourceCache, discovery_trait::{DiscoveryFilter, DiscoveryKind, ResourceDiscovery}, matches_adapter_requirement}, dscresources::adapted_resource_manifest::AdaptedDscResourceManifest, parser::Statement, types::{FullyQualifiedTypeName, TypeNameFilter}}; +use crate::{discovery::{DiscoveryExtensionCache, DiscoveryManifestCache, DiscoveryResourceCache, discovery_trait::{DiscoveryFilter, DiscoveryKind, ResourceDiscovery}, matches_adapter_requirement}, dscresources::{adapted_resource_manifest::AdaptedDscResourceManifest, resource_manifest::SetDeleteArgKind}, parser::Statement, types::{FullyQualifiedTypeName, TypeNameFilter}}; use crate::{locked_clear, locked_is_empty, locked_extend, locked_clone, locked_get}; use crate::configure::{config_doc::ResourceDiscoveryMode, context::Context}; use crate::dscresources::dscresource::{Capability, DscResource, ImplementedAs}; @@ -788,39 +788,51 @@ fn load_resource_manifest(path: &Path, manifest: &ResourceManifest) -> Result = vec![]; + let mut capabilities: HashSet = HashSet::new(); if let Some(get) = &manifest.get { verify_executable(&manifest.resource_type, "get", &get.executable, path.parent().unwrap()); - capabilities.push(Capability::Get); + capabilities.insert(Capability::Get); } if let Some(set) = &manifest.set { verify_executable(&manifest.resource_type, "set", &set.executable, path.parent().unwrap()); - capabilities.push(Capability::Set); + capabilities.insert(Capability::Set); if set.handles_exist == Some(true) { - capabilities.push(Capability::SetHandlesExist); + capabilities.insert(Capability::SetHandlesExist); + } + if let Some(args) = &set.args && args_contains_what_if(args) { + capabilities.insert(Capability::SetWhatIf); } } if let Some(test) = &manifest.test { verify_executable(&manifest.resource_type, "test", &test.executable, path.parent().unwrap()); - capabilities.push(Capability::Test); + capabilities.insert(Capability::Test); } if let Some(delete) = &manifest.delete { verify_executable(&manifest.resource_type, "delete", &delete.executable, path.parent().unwrap()); - capabilities.push(Capability::Delete); + capabilities.insert(Capability::Delete); + if let Some(args) = &delete.args && args_contains_what_if(args) { + capabilities.insert(Capability::DeleteWhatIf); + } } if let Some(export) = &manifest.export { verify_executable(&manifest.resource_type, "export", &export.executable, path.parent().unwrap()); - capabilities.push(Capability::Export); + capabilities.insert(Capability::Export); } if let Some(resolve) = &manifest.resolve { verify_executable(&manifest.resource_type, "resolve", &resolve.executable, path.parent().unwrap()); - capabilities.push(Capability::Resolve); + capabilities.insert(Capability::Resolve); } if let Some(SchemaKind::Command(command)) = &manifest.schema { verify_executable(&manifest.resource_type, "schema", &command.executable, path.parent().unwrap()); } + if let Some(what_if) = &manifest.what_if { + verify_executable(&manifest.resource_type, "what-if", &what_if.executable, path.parent().unwrap()); + capabilities.insert(Capability::SetWhatIf); + } let mut resource = DscResource::new(); + let mut capabilities: Vec = capabilities.into_iter().collect(); + capabilities.sort(); resource.type_name = manifest.resource_type.clone(); resource.kind = kind; resource.implemented_as = Some(ImplementedAs::Command); @@ -835,6 +847,10 @@ fn load_resource_manifest(path: &Path, manifest: &ResourceManifest) -> Result bool { + args.iter().any(|arg| matches!(arg, SetDeleteArgKind::WhatIf{ what_if_arg: _ })) +} + fn load_extension_manifest(path: &Path, manifest: &ExtensionManifest) -> Result { let mut capabilities: Vec = vec![]; if let Some(discover) = &manifest.discover { diff --git a/lib/dsc-lib/src/dscresources/dscresource.rs b/lib/dsc-lib/src/dscresources/dscresource.rs index ef2eacc78..6b5d544e3 100644 --- a/lib/dsc-lib/src/dscresources/dscresource.rs +++ b/lib/dsc-lib/src/dscresources/dscresource.rs @@ -63,7 +63,7 @@ pub struct DscResource { pub manifest: Option, } -#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema)] +#[derive(Clone, Debug, Eq, Hash, PartialEq, Deserialize, Serialize, JsonSchema, DscRepoSchema, Ord, PartialOrd)] #[serde(rename_all = "camelCase")] #[schemars(transform = idiomaticize_string_enum)] #[dsc_repo_schema(base_name = "resourceCapabilities", folder_path = "definitions")] @@ -74,12 +74,14 @@ pub enum Capability { Set, /// The resource supports the `_exist` property directly. SetHandlesExist, - /// The resource supports simulating configuration directly. - WhatIf, + /// The resource supports simulating configuration directly for `set` + SetWhatIf, /// The resource supports validating configuration. Test, /// The resource supports deleting configuration. Delete, + /// The resource supports simulating configuration directly for `delete` + DeleteWhatIf, /// The resource supports exporting configuration. Export, /// The resource supports resolving imported configuration.