diff --git a/dsc/src/subcommand.rs b/dsc/src/subcommand.rs index 25a725b32..4fab13d3b 100644 --- a/dsc/src/subcommand.rs +++ b/dsc/src/subcommand.rs @@ -807,6 +807,7 @@ pub fn list_resources( (Capability::Delete, "d"), (Capability::Export, "e"), (Capability::Resolve, "r"), + (Capability::WhatIf, "w"), ]; let mut capabilities = "-".repeat(capability_types.len()); diff --git a/dsc/tests/dsc_resource_list.tests.ps1 b/dsc/tests/dsc_resource_list.tests.ps1 index 267c72ec5..6d73e86f7 100644 --- a/dsc/tests/dsc_resource_list.tests.ps1 +++ b/dsc/tests/dsc_resource_list.tests.ps1 @@ -161,4 +161,17 @@ Describe 'Tests for listing resources' { $env:DSC_RESOURCE_PATH = $oldPath } } + + It 'What-if capability is added for resources supporting it' { + $out = dsc resource list 'Test/*' | ConvertFrom-Json + $LASTEXITCODE | Should -Be 0 + $out.Count | Should -BeGreaterThan 0 + foreach ($resource in $out) { + if ($resource.type -like 'Test/WhatIf*') { + $resource.capabilities | Should -Contain 'whatIf' + } else { + $resource.capabilities | Should -Not -Contain 'whatIf' + } + } + } } 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..cfa46d7fb 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::WhatIf); } } 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::WhatIf); + } } 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::WhatIf); + } 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..21b246e01 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")]