Skip to content
37 changes: 30 additions & 7 deletions dsc/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<OutputFormat>,
#[clap(short = 'i', long, help = "The input to pass to the configuration or resource", conflicts_with = "input_file")]
pub input: Option<String>,
#[clap(short = 'p', long, help = "The path to a file used as input to the configuration or resource")]
Expand Down Expand Up @@ -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<OutputFormat>,
},
}

#[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<OutputFormat>,
},
#[clap(name = "set", about = "Set the current configuration")]
Set,
Set {
#[clap(short = 'f', long, help = "The output format to use")]
format: Option<OutputFormat>,
},
#[clap(name = "test", about = "Test the current configuration")]
Test,
Test {
#[clap(short = 'f', long, help = "The output format to use")]
format: Option<OutputFormat>,
},
#[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<OutputFormat>,
}
}

#[derive(Debug, PartialEq, Eq, Subcommand)]
Expand All @@ -98,6 +109,8 @@ pub enum ResourceSubCommand {
description: Option<String>,
#[clap(short, long, help = "Tag to search for in the resource tags")]
tags: Option<Vec<String>>,
#[clap(short = 'f', long, help = "The output format to use")]
format: Option<OutputFormat>,
},
#[clap(name = "get", about = "Invoke the get operation to a resource", arg_required_else_help = true)]
Get {
Expand All @@ -107,30 +120,40 @@ pub enum ResourceSubCommand {
resource: String,
#[clap(short, long, help = "The input to pass to the resource as JSON")]
input: Option<String>,
#[clap(short = 'f', long, help = "The output format to use")]
format: Option<OutputFormat>,
},
#[clap(name = "set", about = "Invoke the set operation to a resource", arg_required_else_help = true)]
Set {
#[clap(short, long, help = "The name or DscResource JSON of the resource to invoke `set` on")]
resource: String,
#[clap(short, long, help = "The input to pass to the resource as JSON")]
input: Option<String>,
#[clap(short = 'f', long, help = "The output format to use")]
format: Option<OutputFormat>,
},
#[clap(name = "test", about = "Invoke the test operation to a resource", arg_required_else_help = true)]
Test {
#[clap(short, long, help = "The name or DscResource JSON of the resource to invoke `test` on")]
resource: String,
#[clap(short, long, help = "The input to pass to the resource as JSON")]
input: Option<String>,
#[clap(short = 'f', long, help = "The output format to use")]
format: Option<OutputFormat>,
},
#[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<OutputFormat>,
},
#[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<OutputFormat>,
},
}

Expand Down
10 changes: 5 additions & 5 deletions dsc/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,21 +73,21 @@ 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);
}
}
}
else {
subcommand::config(&subcommand, &parameters, &args.format, &input);
subcommand::config(&subcommand, &parameters, &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,
Expand All @@ -96,7 +96,7 @@ fn main() {
exit(util::EXIT_JSON_ERROR);
}
};
util::write_output(&json, &args.format);
util::write_output(&json, &format);
},
}

Expand Down
24 changes: 12 additions & 12 deletions dsc/src/subcommand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ pub fn config_export(configurator: &mut Configurator, format: &Option<OutputForm
}
}

pub fn config(subcommand: &ConfigSubCommand, parameters: &Option<String>, format: &Option<OutputFormat>, stdin: &Option<String>) {
pub fn config(subcommand: &ConfigSubCommand, parameters: &Option<String>, stdin: &Option<String>) {
let Some(stdin) = stdin else {
error!("Configuration must be piped to STDIN");
exit(EXIT_INVALID_ARGS);
Expand Down Expand Up @@ -184,19 +184,19 @@ pub fn config(subcommand: &ConfigSubCommand, parameters: &Option<String>, format
}

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);
}
}
Expand Down Expand Up @@ -324,7 +324,7 @@ pub fn validate_config(config: &str) {
exit(EXIT_SUCCESS);
}

pub fn resource(subcommand: &ResourceSubCommand, format: &Option<OutputFormat>, stdin: &Option<String>) {
pub fn resource(subcommand: &ResourceSubCommand, stdin: &Option<String>) {
let mut dsc = match DscManager::new() {
Ok(dsc) => dsc,
Err(err) => {
Expand All @@ -334,7 +334,7 @@ pub fn resource(subcommand: &ResourceSubCommand, format: &Option<OutputFormat>,
};

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"]);
Expand Down Expand Up @@ -411,26 +411,26 @@ pub fn resource(subcommand: &ResourceSubCommand, format: &Option<OutputFormat>,
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);
},
Expand Down
2 changes: 1 addition & 1 deletion dsc/tests/dsc_args.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
7 changes: 7 additions & 0 deletions dsc/tests/dsc_config_get.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,11 @@ 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' -Skip:(!$IsWindows) {
$jsonPath = Join-Path $PSScriptRoot '../examples/osinfo_registry.dsc.json'
$config = Get-Content $jsonPath -Raw
$config | dsc config get --format pretty-json | Out-String
$LASTEXITCODE | Should -Be 0
}
}
30 changes: 29 additions & 1 deletion dsc/tests/dsc_export.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}
}
11 changes: 11 additions & 0 deletions dsc/tests/dsc_resource_list.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
}
9 changes: 9 additions & 0 deletions dsc/tests/dsc_schema.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,13 @@ Describe 'config schema tests' {
$schema = $schema | ConvertFrom-Json
$schema.'$schema' | Should -BeExactly 'http://json-schema.org/draft-07/schema#'
}

It 'can accept the use of --format as a subcommand' -Skip:(!$IsWindows) {

$schema = dsc schema -t get-result -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#'
}
}
43 changes: 43 additions & 0 deletions dsc/tests/dsc_set.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,49 @@ Describe 'config set tests' {
($result.psobject.properties | Measure-Object).Count | Should -Be 3
}

It 'can accept the use of --format <format> as a subcommand' -Skip:(!$IsWindows) -TestCases @(
@{ format = 'yaml'; expected = @'
beforeState:
$id: https://developer.microsoft.com/json-schemas/windows/registry/20230303/Microsoft.Windows.Registry.schema.json
keyPath: HKCU\1
_exist: false
afterState:
$id: https://developer.microsoft.com/json-schemas/windows/registry/20230303/Microsoft.Windows.Registry.schema.json
keyPath: HKCU\1
_exist: false
changedProperties: []
'@ }
@{ format = 'json'; expected = '{"beforeState":{"$id":"https://developer.microsoft.com/json-schemas/windows/registry/20230303/Microsoft.Windows.Registry.schema.json","keyPath":"HKCU\\1","_exist":false},"afterState":{"$id":"https://developer.microsoft.com/json-schemas/windows/registry/20230303/Microsoft.Windows.Registry.schema.json","keyPath":"HKCU\\1","_exist":false},"changedProperties":[]}' }
@{ format = 'pretty-json'; expected = @'
{
"beforeState": {
"$id": "https://developer.microsoft.com/json-schemas/windows/registry/20230303/Microsoft.Windows.Registry.schema.json",
"keyPath": "HKCU\\1",
"_exist": false
},
"afterState": {
"$id": "https://developer.microsoft.com/json-schemas/windows/registry/20230303/Microsoft.Windows.Registry.schema.json",
"keyPath": "HKCU\\1",
"_exist": false
},
"changedProperties": []
}
'@ }
) {
param($format, $expected)

$json = @'
{
"keyPath": "HKCU\\1",
"_exist": false
}
'@

$out = $json | dsc resource set -r Microsoft.Windows/registry --format $format | Out-String
$LASTEXITCODE | Should -Be 0
$out.Trim() | Should -BeExactly $expected
}

It 'set can be used on a resource that does not implement test' {
$manifest = @'
{
Expand Down
12 changes: 12 additions & 0 deletions dsc/tests/dsc_test.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,16 @@ Describe 'config test tests' {
$out.differingProperties[1] | Should -BeExactly 'valueData'
$out.differingProperties[2] | Should -BeExactly '_exist'
}

It 'can accept the use of --format as a subcommand' -Skip:(!$IsWindows) {
$json = @'
{
"keyPath": "HKLM\\Software\\Microsoft\\Windows NT\\CurrentVersion",
"valueName": "ProductName"
}
'@
$current = $json | registry config get
$current | dsc resource test -r Microsoft.Windows/registry --format pretty-json
$LASTEXITCODE | Should -Be 0
}
}