diff --git a/dsc/src/subcommand.rs b/dsc/src/subcommand.rs index 81ced75f4..9f87bdaf9 100644 --- a/dsc/src/subcommand.rs +++ b/dsc/src/subcommand.rs @@ -14,7 +14,7 @@ use dsc_lib::dscresources::invoke_result::{ use dsc_lib::{ DscManager, dscresources::invoke_result::ValidateResult, - dscresources::dscresource::{ImplementedAs, Invoke}, + dscresources::dscresource::{Capability, ImplementedAs, Invoke}, dscresources::resource_manifest::{import_manifest, ResourceManifest}, }; use serde_yaml::Value; @@ -400,13 +400,17 @@ pub fn resource(subcommand: &ResourceSubCommand, stdin: &Option) { ResourceSubCommand::List { resource_name, description, tags, format } => { let mut write_table = false; - let mut table = Table::new(&["Type", "Kind", "Version", "Methods", "Requires", "Description"]); + let mut table = Table::new(&["Type", "Kind", "Version", "Caps", "Requires", "Description"]); if format.is_none() && atty::is(Stream::Stdout) { // write as table if format is not specified and interactive write_table = true; } for resource in dsc.list_available_resources(&resource_name.clone().unwrap_or_default()) { - let mut methods = "g---".to_string(); + let mut capabilities = "g---".to_string(); + if resource.capabilities.contains(&Capability::Set) { capabilities.replace_range(1..2, "s"); } + if resource.capabilities.contains(&Capability::Test) { capabilities.replace_range(2..3, "t"); } + if resource.capabilities.contains(&Capability::Export) { capabilities.replace_range(3..4, "e"); } + // if description, tags, or write_table is specified, pull resource manifest if it exists if let Some(ref resource_manifest) = resource.manifest { let manifest = match import_manifest(resource_manifest.clone()) { @@ -438,10 +442,6 @@ pub fn resource(subcommand: &ResourceSubCommand, stdin: &Option) { } if !found { continue; } } - - if manifest.set.is_some() { methods.replace_range(1..2, "s"); } - if manifest.test.is_some() { methods.replace_range(2..3, "t"); } - if manifest.export.is_some() { methods.replace_range(3..4, "e"); } } else { // resource does not have a manifest but filtering on description or tags was requested - skip such resource if description.is_some() || tags.is_some() { @@ -454,7 +454,7 @@ pub fn resource(subcommand: &ResourceSubCommand, stdin: &Option) { resource.type_name, format!("{:?}", resource.kind), resource.version, - methods, + capabilities, resource.requires.unwrap_or_default(), resource.description.unwrap_or_default() ]); diff --git a/dsc/tests/dsc_resource_list.tests.ps1 b/dsc/tests/dsc_resource_list.tests.ps1 index 2eebdf808..2e7aa40e7 100644 --- a/dsc/tests/dsc_resource_list.tests.ps1 +++ b/dsc/tests/dsc_resource_list.tests.ps1 @@ -53,4 +53,12 @@ Describe 'Tests for listing resources' { $resources.type | Should -BeExactly $expectedType } } + + It 'Capabilities are returned' { + $resource = dsc resource list Microsoft/OSInfo | ConvertFrom-Json + $LASTEXITCODE | Should -Be 0 + $resource.capabilities.Count | Should -Be 2 + $resource.capabilities | Should -Contain 'Get' + $resource.capabilities | Should -Contain 'Export' + } } diff --git a/dsc_lib/src/discovery/command_discovery.rs b/dsc_lib/src/discovery/command_discovery.rs index ddd6194d4..1a59ec69f 100644 --- a/dsc_lib/src/discovery/command_discovery.rs +++ b/dsc_lib/src/discovery/command_discovery.rs @@ -2,7 +2,7 @@ // Licensed under the MIT License. use crate::discovery::discovery_trait::ResourceDiscovery; -use crate::dscresources::dscresource::{DscResource, ImplementedAs}; +use crate::dscresources::dscresource::{Capability, DscResource, ImplementedAs}; use crate::dscresources::resource_manifest::{import_manifest, Kind, ResourceManifest}; use crate::dscresources::command_resource::invoke_command; use crate::dscerror::DscError; @@ -281,12 +281,25 @@ fn load_manifest(path: &Path) -> Result { Kind::Resource }; + // all command based resources are required to support `get` + let mut capabilities = vec![Capability::Get]; + if manifest.set.is_some() { + capabilities.push(Capability::Set); + } + if manifest.test.is_some() { + capabilities.push(Capability::Test); + } + if manifest.export.is_some() { + capabilities.push(Capability::Export); + } + let resource = DscResource { type_name: manifest.resource_type.clone(), kind, implemented_as: ImplementedAs::Command, description: manifest.description.clone(), version: manifest.version.clone(), + capabilities, path: path.to_str().unwrap().to_string(), directory: path.parent().unwrap().to_str().unwrap().to_string(), manifest: Some(serde_json::to_value(manifest)?), diff --git a/dsc_lib/src/dscresources/dscresource.rs b/dsc_lib/src/dscresources/dscresource.rs index ebe01a487..2ca9f990c 100644 --- a/dsc_lib/src/dscresources/dscresource.rs +++ b/dsc_lib/src/dscresources/dscresource.rs @@ -21,6 +21,8 @@ pub struct DscResource { pub kind: Kind, /// The version of the resource. pub version: String, + /// The capabilities of the resource. + pub capabilities: Vec, /// The file path to the resource. pub path: String, /// The description of the resource. @@ -40,6 +42,14 @@ pub struct DscResource { pub manifest: Option, } +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize, JsonSchema)] +pub enum Capability { + Get, + Set, + Test, + Export, +} + #[derive(Clone, Debug, PartialEq, Deserialize, Serialize, JsonSchema)] #[serde(untagged)] pub enum ImplementedAs { @@ -56,6 +66,7 @@ impl DscResource { type_name: String::new(), kind: Kind::Resource, version: String::new(), + capabilities: Vec::new(), description: None, path: String::new(), directory: String::new(), diff --git a/powershell-adapter/powershell.resource.ps1 b/powershell-adapter/powershell.resource.ps1 index 9293d5b94..e307519ae 100644 --- a/powershell-adapter/powershell.resource.ps1 +++ b/powershell-adapter/powershell.resource.ps1 @@ -106,12 +106,21 @@ if ($Operation -eq 'List') $fullResourceTypeName = "$moduleName/$($r.ResourceType)" $script:ResourceCache[$fullResourceTypeName] = $r - if ($WinPS) {$requiresString = "Microsoft.Windows/WindowsPowerShell"} else {$requiresString = "Microsoft.DSC/PowerShell"} + if ($WinPS) {$requiresString = "Microsoft.Windows/WindowsPowerShell"} else {$requiresString = "Microsoft.DSC/PowerShell"} + + $t = [Type]$r.ResourceType + $exportMethod = $t.GetMethod('Export') + + $capabilities = @('Get', 'Set', 'Test') + if ($null -ne $exportMethod) { + $capabilities += 'Export' + } $z = [pscustomobject]@{ type = $fullResourceTypeName; kind = 'Resource'; version = $version_string; + capabilities = $capabilities; path = $r.Path; directory = $r.ParentPath; implementedAs = $r.ImplementationDetail; diff --git a/tools/test_group_resource/src/main.rs b/tools/test_group_resource/src/main.rs index dd119e571..a70d1dfb4 100644 --- a/tools/test_group_resource/src/main.rs +++ b/tools/test_group_resource/src/main.rs @@ -6,7 +6,7 @@ mod args; use args::{Args, SubCommand}; use clap::Parser; use dsc_lib::dscresources::resource_manifest::{ResourceManifest, GetMethod, Kind}; -use dsc_lib::dscresources::dscresource::{DscResource, ImplementedAs}; +use dsc_lib::dscresources::dscresource::{Capability, DscResource, ImplementedAs}; fn main() { let args = Args::parse(); @@ -16,6 +16,7 @@ fn main() { type_name: "Test/TestResource1".to_string(), kind: Kind::Resource, version: "1.0.0".to_string(), + capabilities: vec![Capability::Get, Capability::Set], description: Some("This is a test resource.".to_string()), implemented_as: ImplementedAs::Custom("TestResource".to_string()), path: "test_resource1".to_string(), @@ -48,6 +49,7 @@ fn main() { type_name: "Test/TestResource2".to_string(), kind: Kind::Resource, version: "1.0.1".to_string(), + capabilities: vec![Capability::Get, Capability::Set], description: Some("This is a test resource.".to_string()), implemented_as: ImplementedAs::Custom("TestResource".to_string()), path: "test_resource2".to_string(), @@ -84,6 +86,7 @@ fn main() { type_name: "InvalidResource".to_string(), kind: Kind::Resource, version: "1.0.0".to_string(), + capabilities: vec![Capability::Get], description: Some("This is a test resource.".to_string()), implemented_as: ImplementedAs::Custom("TestResource".to_string()), path: "test_resource1".to_string(), diff --git a/wmi-adapter/wmi.resource.ps1 b/wmi-adapter/wmi.resource.ps1 index dbf62069b..d4c3ea49c 100644 --- a/wmi-adapter/wmi.resource.ps1 +++ b/wmi-adapter/wmi.resource.ps1 @@ -49,6 +49,7 @@ if ($Operation -eq 'List') type = $fullResourceTypeName; kind = 'Resource'; version = $version_string; + capabilities = @('Get'); path = ""; directory = ""; implementedAs = "";