diff --git a/dsc/src/args.rs b/dsc/src/args.rs index c3920d14a..58bff0b41 100644 --- a/dsc/src/args.rs +++ b/dsc/src/args.rs @@ -33,9 +33,6 @@ pub struct Args { /// The subcommand to run #[clap(subcommand)] pub subcommand: SubCommand, - /// The output format to use - #[clap(short = 'o', long)] - pub format: Option, #[clap(short = 'i', long, help = "The input to pass to the configuration or resource", conflicts_with = "input_file")] pub input: Option, #[clap(short = 'p', long, help = "The path to a file used as input to the configuration or resource")] @@ -71,21 +68,35 @@ pub enum SubCommand { Schema { #[clap(name = "type", short, long, help = "The type of DSC schema to get")] dsc_type: DscType, + #[clap(short = 'f', long, help = "The output format to use")] + format: Option, }, } #[derive(Debug, PartialEq, Eq, Subcommand)] pub enum ConfigSubCommand { #[clap(name = "get", about = "Retrieve the current configuration")] - Get, + Get { + #[clap(short = 'f', long, help = "The output format to use")] + format: Option, + }, #[clap(name = "set", about = "Set the current configuration")] - Set, + Set { + #[clap(short = 'f', long, help = "The output format to use")] + format: Option, + }, #[clap(name = "test", about = "Test the current configuration")] - Test, + Test { + #[clap(short = 'f', long, help = "The output format to use")] + format: Option, + }, #[clap(name = "validate", about = "Validate the current configuration", hide = true)] Validate, #[clap(name = "export", about = "Export the current configuration")] - Export + Export { + #[clap(short = 'f', long, help = "The output format to use")] + format: Option, + } } #[derive(Debug, PartialEq, Eq, Subcommand)] @@ -98,6 +109,8 @@ pub enum ResourceSubCommand { description: Option, #[clap(short, long, help = "Tag to search for in the resource tags")] tags: Option>, + #[clap(short = 'f', long, help = "The output format to use")] + format: Option, }, #[clap(name = "get", about = "Invoke the get operation to a resource", arg_required_else_help = true)] Get { @@ -107,6 +120,8 @@ pub enum ResourceSubCommand { resource: String, #[clap(short, long, help = "The input to pass to the resource as JSON")] input: Option, + #[clap(short = 'f', long, help = "The output format to use")] + format: Option, }, #[clap(name = "set", about = "Invoke the set operation to a resource", arg_required_else_help = true)] Set { @@ -114,6 +129,8 @@ pub enum ResourceSubCommand { resource: String, #[clap(short, long, help = "The input to pass to the resource as JSON")] input: Option, + #[clap(short = 'f', long, help = "The output format to use")] + format: Option, }, #[clap(name = "test", about = "Invoke the test operation to a resource", arg_required_else_help = true)] Test { @@ -121,16 +138,22 @@ pub enum ResourceSubCommand { resource: String, #[clap(short, long, help = "The input to pass to the resource as JSON")] input: Option, + #[clap(short = 'f', long, help = "The output format to use")] + format: Option, }, #[clap(name = "schema", about = "Get the JSON schema for a resource", arg_required_else_help = true)] Schema { #[clap(short, long, help = "The name of the resource to get the JSON schema")] resource: String, + #[clap(short = 'f', long, help = "The output format to use")] + format: Option, }, #[clap(name = "export", about = "Retrieve all resource instances", arg_required_else_help = true)] Export { #[clap(short, long, help = "The name or DscResource JSON of the resource to invoke `export` on")] resource: String, + #[clap(short = 'f', long, help = "The output format to use")] + format: Option, }, } diff --git a/dsc/src/main.rs b/dsc/src/main.rs index 5eafb4e24..db1e53359 100644 --- a/dsc/src/main.rs +++ b/dsc/src/main.rs @@ -73,7 +73,7 @@ fn main() { if let Some(file_name) = parameters_file { info!("Reading parameters from file {}", file_name); match std::fs::read_to_string(file_name) { - Ok(parameters) => subcommand::config(&subcommand, &Some(parameters), &args.format, &input), + Ok(parameters) => subcommand::config(&subcommand, &Some(parameters), &input), Err(err) => { error!("Error: Failed to read parameters file: {err}"); exit(util::EXIT_INVALID_INPUT); @@ -81,13 +81,13 @@ fn main() { } } else { - subcommand::config(&subcommand, ¶meters, &args.format, &input); + subcommand::config(&subcommand, ¶meters, &input); } }, SubCommand::Resource { subcommand } => { - subcommand::resource(&subcommand, &args.format, &input); + subcommand::resource(&subcommand, &input); }, - SubCommand::Schema { dsc_type } => { + SubCommand::Schema { dsc_type , format } => { let schema = util::get_schema(dsc_type); let json = match serde_json::to_string(&schema) { Ok(json) => json, @@ -96,7 +96,7 @@ fn main() { exit(util::EXIT_JSON_ERROR); } }; - util::write_output(&json, &args.format); + util::write_output(&json, &format); }, } diff --git a/dsc/src/subcommand.rs b/dsc/src/subcommand.rs index a269ebca5..376a50ad0 100644 --- a/dsc/src/subcommand.rs +++ b/dsc/src/subcommand.rs @@ -116,7 +116,7 @@ pub fn config_export(configurator: &mut Configurator, format: &Option, format: &Option, stdin: &Option) { +pub fn config(subcommand: &ConfigSubCommand, parameters: &Option, stdin: &Option) { let Some(stdin) = stdin else { error!("Configuration must be piped to STDIN"); exit(EXIT_INVALID_ARGS); @@ -179,24 +179,24 @@ pub fn config(subcommand: &ConfigSubCommand, parameters: &Option, format }; if let Err(err) = configurator.set_parameters(¶meters) { - error!("Error: Paramter input failure: {err}"); + error!("Error: Parameter input failure: {err}"); exit(EXIT_INVALID_INPUT); } match subcommand { - ConfigSubCommand::Get => { + ConfigSubCommand::Get { format } => { config_get(&mut configurator, format); }, - ConfigSubCommand::Set => { + ConfigSubCommand::Set { format } => { config_set(&mut configurator, format); }, - ConfigSubCommand::Test => { + ConfigSubCommand::Test { format } => { config_test(&mut configurator, format); }, ConfigSubCommand::Validate => { validate_config(&json_string); }, - ConfigSubCommand::Export => { + ConfigSubCommand::Export { format } => { config_export(&mut configurator, format); } } @@ -324,7 +324,7 @@ pub fn validate_config(config: &str) { exit(EXIT_SUCCESS); } -pub fn resource(subcommand: &ResourceSubCommand, format: &Option, stdin: &Option) { +pub fn resource(subcommand: &ResourceSubCommand, stdin: &Option) { let mut dsc = match DscManager::new() { Ok(dsc) => dsc, Err(err) => { @@ -334,7 +334,7 @@ pub fn resource(subcommand: &ResourceSubCommand, format: &Option, }; match subcommand { - ResourceSubCommand::List { resource_name, description, tags } => { + ResourceSubCommand::List { resource_name, description, tags, format } => { let mut write_table = false; let mut table = Table::new(&["Type", "Version", "Requires", "Description"]); @@ -411,26 +411,26 @@ pub fn resource(subcommand: &ResourceSubCommand, format: &Option, table.print(); } }, - ResourceSubCommand::Get { resource, input, all } => { + ResourceSubCommand::Get { resource, input, all, format } => { dsc.discover_resources(&[resource.to_lowercase().to_string()]); if *all { resource_command::get_all(&dsc, resource, input, stdin, format); } else { resource_command::get(&dsc, resource, input, stdin, format); }; }, - ResourceSubCommand::Set { resource, input } => { + ResourceSubCommand::Set { resource, input, format } => { dsc.discover_resources(&[resource.to_lowercase().to_string()]); resource_command::set(&dsc, resource, input, stdin, format); }, - ResourceSubCommand::Test { resource, input } => { + ResourceSubCommand::Test { resource, input, format } => { dsc.discover_resources(&[resource.to_lowercase().to_string()]); resource_command::test(&dsc, resource, input, stdin, format); }, - ResourceSubCommand::Schema { resource } => { + ResourceSubCommand::Schema { resource , format } => { dsc.discover_resources(&[resource.to_lowercase().to_string()]); resource_command::schema(&dsc, resource, format); }, - ResourceSubCommand::Export { resource} => { + ResourceSubCommand::Export { resource, format } => { dsc.discover_resources(&[resource.to_lowercase().to_string()]); resource_command::export(&mut dsc, resource, format); }, diff --git a/dsc/tests/dsc_args.tests.ps1 b/dsc/tests/dsc_args.tests.ps1 index 339191d42..94db1a92a 100644 --- a/dsc/tests/dsc_args.tests.ps1 +++ b/dsc/tests/dsc_args.tests.ps1 @@ -85,7 +85,7 @@ actualState: ) { param($format, $expected) - $out = dsc --format $format resource get -r Test/Hello | Out-String + $out = dsc resource get -r Test/Hello --format $format | Out-String $LASTEXITCODE | Should -Be 0 $out.Trim() | Should -BeExactly $expected } diff --git a/dsc/tests/dsc_config_get.tests.ps1 b/dsc/tests/dsc_config_get.tests.ps1 index 61308b78d..2bf907c99 100644 --- a/dsc/tests/dsc_config_get.tests.ps1 +++ b/dsc/tests/dsc_config_get.tests.ps1 @@ -30,4 +30,17 @@ Describe 'dsc config get tests' { $null = $config | dsc config get | ConvertFrom-Json $LASTEXITCODE | Should -Be 2 } + + It 'can accept the use of --format as a subcommand' { + $config_yaml = @" + `$schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/10/config/document.json + resources: + - name: Echo + type: Test/Echo + properties: + text: hello +"@ + $null = $config_yaml | dsc config get --format pretty-json | Out-String + $LASTEXITCODE | Should -Be 0 + } } diff --git a/dsc/tests/dsc_export.tests.ps1 b/dsc/tests/dsc_export.tests.ps1 index 20f834cdb..a930cec4b 100644 --- a/dsc/tests/dsc_export.tests.ps1 +++ b/dsc/tests/dsc_export.tests.ps1 @@ -2,7 +2,7 @@ # Licensed under the MIT License. Describe 'resource export tests' { - + It 'Export can be called on individual resource' { $out = dsc resource export -r Microsoft/Process @@ -74,4 +74,32 @@ Describe 'resource export tests' { $LASTEXITCODE | Should -Be 2 $out | out-string | Should -BeLike '*specified multiple times*' } + + It 'Export can be called on individual resource with the use of --format as a subcommand' { + + $out = dsc resource export -r Microsoft/Process -f pretty-json + $LASTEXITCODE | Should -Be 0 + $config_with_process_list = $out | ConvertFrom-Json + $config_with_process_list.'$schema' | Should -BeExactly 'https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json' + $config_with_process_list.'resources' | Should -Not -BeNullOrEmpty + $config_with_process_list.resources.count | Should -BeGreaterThan 1 + } + + It 'Export can be called on a configuration with the use of --format as a subcommand' { + + $yaml = @' + $schema: https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/10/config/document.json + resources: + - name: Processes + type: Microsoft/Process + properties: + pid: 0 +'@ + $out = $yaml | dsc config export -f pretty-json + $LASTEXITCODE | Should -Be 0 + $config_with_process_list = $out | ConvertFrom-Json + $config_with_process_list.'$schema' | Should -BeExactly 'https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/config/document.json' + $config_with_process_list.'resources' | Should -Not -BeNullOrEmpty + $config_with_process_list.resources.count | Should -BeGreaterThan 1 + } } diff --git a/dsc/tests/dsc_resource_list.tests.ps1 b/dsc/tests/dsc_resource_list.tests.ps1 index b3b4a5937..3d3d9cc96 100644 --- a/dsc/tests/dsc_resource_list.tests.ps1 +++ b/dsc/tests/dsc_resource_list.tests.ps1 @@ -39,4 +39,15 @@ Describe 'Tests for listing resources' { $resources.type | Should -BeExactly $expectedType } } + + It 'can accept the use of --format as a subcommand' { + $expectedCount = 1 + $expectedType = 'Microsoft/OSInfo' + $resources = dsc resource list --description "operating system" --format pretty-json | ConvertFrom-Json + $LASTEXITCODE | Should -Be 0 + $resources.Count | Should -Be $expectedCount + if ($expectedCount -gt 0) { + $resources.type | Should -BeExactly $expectedType + } + } } diff --git a/dsc/tests/dsc_schema.tests.ps1 b/dsc/tests/dsc_schema.tests.ps1 index 7c07771d4..48c308bc4 100644 --- a/dsc/tests/dsc_schema.tests.ps1 +++ b/dsc/tests/dsc_schema.tests.ps1 @@ -28,4 +28,12 @@ Describe 'config schema tests' { $schema = $schema | ConvertFrom-Json $schema.'$schema' | Should -BeExactly 'http://json-schema.org/draft-07/schema#' } -} \ No newline at end of file + + It 'can accept the use of --format as a subcommand' { + $schema = dsc resource schema -r Test/Echo -f pretty-json + $LASTEXITCODE | Should -Be 0 + $schema | Should -Not -BeNullOrEmpty + $schema = $schema | ConvertFrom-Json + $schema.'$schema' | Should -BeExactly 'http://json-schema.org/draft-07/schema#' + } +} diff --git a/dsc/tests/dsc_set.tests.ps1 b/dsc/tests/dsc_set.tests.ps1 index 8be4d4d33..0bda542bb 100644 --- a/dsc/tests/dsc_set.tests.ps1 +++ b/dsc/tests/dsc_set.tests.ps1 @@ -2,6 +2,57 @@ # Licensed under the MIT License. Describe 'config set tests' { + BeforeAll { + $manifest = @' + { + "$schema": "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/bundled/resource/manifest.json", + "type": "Test/SetNoTest", + "version": "0.1.0", + "get": { + "executable": "pwsh", + "args": [ + "-NoLogo", + "-NonInteractive", + "-NoProfile", + "-Command", + "'{ \"test\": true }'" + ] + }, + "set": { + "executable": "pwsh", + "input": "stdin", + "args": [ + "-NoLogo", + "-NonInteractive", + "-NoProfile", + "-Command", + "'{ \"test\": false }'" + ], + "return": "state" + }, + "schema": { + "embedded": { + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://test", + "title": "test", + "description": "test", + "type": "object", + "required": [], + "additionalProperties": false, + "properties": { + "test": { + "type": "boolean", + "description": "test" + } + } + } + } + } +'@ + + Set-Content -Path "$TestDrive/SetNoTest.dsc.resource.json" -Value $manifest + } + BeforeEach { if ($IsWindows) { $json = @' @@ -67,55 +118,45 @@ Describe 'config set tests' { ($result.psobject.properties | Measure-Object).Count | Should -Be 3 } - It 'set can be used on a resource that does not implement test' { - $manifest = @' - { - "$schema": "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/2023/08/bundled/resource/manifest.json", - "type": "Test/SetNoTest", - "version": "0.1.0", - "get": { - "executable": "pwsh", - "args": [ - "-NoLogo", - "-NonInteractive", - "-NoProfile", - "-Command", - "'{ \"test\": true }'" - ] - }, - "set": { - "executable": "pwsh", - "input": "stdin", - "args": [ - "-NoLogo", - "-NonInteractive", - "-NoProfile", - "-Command", - "'{ \"test\": false }'" - ], - "return": "state" - }, - "schema": { - "embedded": { - "$schema": "http://json-schema.org/draft-07/schema#", - "$id": "https://test", - "title": "test", - "description": "test", - "type": "object", - "required": [], - "additionalProperties": false, - "properties": { - "test": { - "type": "boolean", - "description": "test" - } - } - } - } + It 'can accept the use of --format as a subcommand' -Skip:(!$IsWindows) -TestCases @( + @{ format = 'yaml'; expected = @' +beforeState: + test: true +afterState: + test: false +changedProperties: +- test +'@ } + @{ format = 'json'; expected = '{"beforeState":{"test":true},"afterState":{"test":false},"changedProperties":["test"]}' } + @{ format = 'pretty-json'; expected = @' +{ + "beforeState": { + "test": true + }, + "afterState": { + "test": false + }, + "changedProperties": [ + "test" + ] +} +'@ } + ) { + param($format, $expected) + + $oldPath = $env:DSC_RESOURCE_PATH + try { + $env:DSC_RESOURCE_PATH = $TestDrive + $out = '{ "test": true }' | dsc resource set -r Test/SetNoTest --format $format | Out-String + $LASTEXITCODE | Should -Be 0 + $out.Trim() | Should -BeExactly $expected } -'@ + finally { + $env:DSC_RESOURCE_PATH = $oldPath + } + } - Set-Content -Path "$TestDrive/SetNoTest.dsc.resource.json" -Value $manifest + It 'set can be used on a resource that does not implement test' { $oldPath = $env:DSC_RESOURCE_PATH try { $env:DSC_RESOURCE_PATH = $TestDrive diff --git a/dsc/tests/dsc_test.tests.ps1 b/dsc/tests/dsc_test.tests.ps1 index 8e602b921..da3e51494 100644 --- a/dsc/tests/dsc_test.tests.ps1 +++ b/dsc/tests/dsc_test.tests.ps1 @@ -54,4 +54,9 @@ Describe 'config test tests' { $out.differingProperties[1] | Should -BeExactly 'valueData' $out.differingProperties[2] | Should -BeExactly '_exist' } -} \ No newline at end of file + + It 'can accept the use of --format as a subcommand' { + $null = "text: hello" | dsc resource test -r Test/Echo --format pretty-json + $LASTEXITCODE | Should -Be 0 + } +}