Skip to content

Commit

Permalink
Merge branch 'main' into maint/main/document-dependsOn-update
Browse files Browse the repository at this point in the history
  • Loading branch information
SteveL-MSFT authored Sep 5, 2023
2 parents 467bc33 + 415d60e commit e1b30f9
Show file tree
Hide file tree
Showing 14 changed files with 447 additions and 16 deletions.
9 changes: 9 additions & 0 deletions dsc/src/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ pub enum ConfigSubCommand {
Test,
#[clap(name = "validate", about = "Validate the current configuration", hide = true)]
Validate,
#[clap(name = "export", about = "Export the current configuration")]
Export
}

#[derive(Debug, PartialEq, Eq, Subcommand)]
Expand All @@ -65,6 +67,8 @@ pub enum ResourceSubCommand {
},
#[clap(name = "get", about = "Invoke the get operation to a resource", arg_required_else_help = true)]
Get {
#[clap(short, long, help = "Get all instances of the resource")]
all: bool,
#[clap(short, long, help = "The name or DscResource JSON of the resource to invoke `get` on")]
resource: String,
#[clap(short, long, help = "The input to pass to the resource as JSON")]
Expand All @@ -89,6 +93,11 @@ pub enum ResourceSubCommand {
#[clap(short, long, help = "The name of the resource to get the JSON schema")]
resource: String,
},
#[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,
},
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum)]
Expand Down
48 changes: 48 additions & 0 deletions dsc/src/resource_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

use crate::args::OutputFormat;
use crate::util::{EXIT_DSC_ERROR, EXIT_INVALID_ARGS, EXIT_JSON_ERROR, add_type_name_to_json, write_output};
use dsc_lib::configure::config_doc::Configuration;
use dsc_lib::configure::add_resource_export_results_to_configuration;
use dsc_lib::dscresources::invoke_result::GetResult;

use dsc_lib::{
dscresources::dscresource::{Invoke, DscResource},
Expand Down Expand Up @@ -42,6 +45,34 @@ pub fn get(dsc: &mut DscManager, resource: &str, input: &Option<String>, stdin:
}
}

pub fn get_all(dsc: &mut DscManager, resource: &str, _input: &Option<String>, _stdin: &Option<String>, format: &Option<OutputFormat>) {
let resource = get_resource(dsc, resource);

let export_result = match resource.export() {
Ok(export) => { export }
Err(err) => {
eprintln!("Error: {err}");
exit(EXIT_DSC_ERROR);
}
};

for instance in export_result.actual_state
{
let get_result = GetResult {
actual_state: instance.clone(),
};

let json = match serde_json::to_string(&get_result) {
Ok(json) => json,
Err(err) => {
eprintln!("JSON Error: {err}");
exit(EXIT_JSON_ERROR);
}
};
write_output(&json, format);
}
}

pub fn set(dsc: &mut DscManager, resource: &str, input: &Option<String>, stdin: &Option<String>, format: &Option<OutputFormat>) {
let mut input = get_input(input, stdin);
let mut resource = get_resource(dsc, resource);
Expand Down Expand Up @@ -129,6 +160,23 @@ pub fn schema(dsc: &mut DscManager, resource: &str, format: &Option<OutputFormat
}
}

pub fn export(dsc: &mut DscManager, resource: &str, format: &Option<OutputFormat>) {
let dsc_resource = get_resource(dsc, resource);

let mut conf = Configuration::new();

add_resource_export_results_to_configuration(&dsc_resource, &mut conf);

let json = match serde_json::to_string(&conf) {
Ok(json) => json,
Err(err) => {
eprintln!("JSON Error: {err}");
exit(EXIT_JSON_ERROR);
}
};
write_output(&json, format);
}

pub fn get_resource(dsc: &mut DscManager, resource: &str) -> DscResource {
// check if resource is JSON or just a name
match serde_json::from_str(resource) {
Expand Down
40 changes: 38 additions & 2 deletions dsc/src/subcommand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,35 @@ pub fn config_test(configurator: Configurator, format: &Option<OutputFormat>)
}
}

pub fn config_export(configurator: Configurator, format: &Option<OutputFormat>)
{
match configurator.invoke_export(ErrorAction::Continue, || { /* code */ }) {
Ok(result) => {
let json = match serde_json::to_string(&result.result) {
Ok(json) => json,
Err(err) => {
eprintln!("JSON Error: {err}");
exit(EXIT_JSON_ERROR);
}
};
write_output(&json, format);
if result.had_errors {

for msg in result.messages
{
eprintln!("{:?} message {}", msg.level, msg.message);
};

exit(EXIT_DSC_ERROR);
}
},
Err(err) => {
eprintln!("Error: {err}");
exit(EXIT_DSC_ERROR);
}
}
}

pub fn config(subcommand: &ConfigSubCommand, format: &Option<OutputFormat>, stdin: &Option<String>) {
if stdin.is_none() {
eprintln!("Configuration must be piped to STDIN");
Expand Down Expand Up @@ -134,6 +163,9 @@ pub fn config(subcommand: &ConfigSubCommand, format: &Option<OutputFormat>, stdi
},
ConfigSubCommand::Validate => {
validate_config(&json_string);
},
ConfigSubCommand::Export => {
config_export(configurator, format);
}
}
}
Expand Down Expand Up @@ -353,8 +385,9 @@ pub fn resource(subcommand: &ResourceSubCommand, format: &Option<OutputFormat>,
table.print();
}
},
ResourceSubCommand::Get { resource, input } => {
resource_command::get(&mut dsc, resource, input, stdin, format);
ResourceSubCommand::Get { resource, input, all } => {
if *all { resource_command::get_all(&mut dsc, resource, input, stdin, format); }
else { resource_command::get(&mut dsc, resource, input, stdin, format); };
},
ResourceSubCommand::Set { resource, input } => {
resource_command::set(&mut dsc, resource, input, stdin, format);
Expand All @@ -365,5 +398,8 @@ pub fn resource(subcommand: &ResourceSubCommand, format: &Option<OutputFormat>,
ResourceSubCommand::Schema { resource } => {
resource_command::schema(&mut dsc, resource, format);
},
ResourceSubCommand::Export { resource} => {
resource_command::export(&mut dsc, resource, format);
},
}
}
77 changes: 77 additions & 0 deletions dsc/tests/dsc_export.tests.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

Describe 'resource export tests' {

It 'Export can be called on individual resource' {

$out = dsc resource export -r Microsoft/Process
$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 'get --all can be called on individual resource' {

$out = dsc resource get --all -r Microsoft/Process
$LASTEXITCODE | Should -Be 0
$process_list = $out | ConvertFrom-Json
$process_list.resources.count | Should -BeGreaterThan 1
$process_list | % {$_.actualState | Should -Not -BeNullOrEmpty}
}

It 'Export can be called on a configuration' {

$yaml = @'
$schema: https://schemas.microsoft.com/dsc/2023/03/configuration.schema.json
resources:
- name: Processes
type: Microsoft/Process
properties:
pid: 0
'@
$out = $yaml | dsc config export
$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 'Configuration Export can be piped to configuration Set' -Skip:(!$IsWindows) {

$yaml = @'
$schema: https://schemas.microsoft.com/dsc/2023/03/configuration.schema.json
resources:
- name: Processes
type: Microsoft/Process
properties:
pid: 0
'@
$out = $yaml | dsc config export | dsc config set
$LASTEXITCODE | Should -Be 0
$set_results = $out | ConvertFrom-Json
$set_results.results.count | Should -BeGreaterThan 1
}

It 'Duplicate resource types in Configuration Export should result in error' {

$yaml = @'
$schema: https://schemas.microsoft.com/dsc/2023/03/configuration.schema.json
resources:
- name: Processes
type: Microsoft/Process
properties:
pid: 0
- name: Processes
type: Microsoft/Process
properties:
pid: 0
'@
$out = $yaml | dsc config export 2>&1
$LASTEXITCODE | Should -Be 2
$out | Should -BeLike '*specified multiple times*'
}
}
31 changes: 31 additions & 0 deletions dsc_lib/src/configure/config_doc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,34 @@ impl Default for Configuration {
}
}
}

impl Configuration {
#[must_use]
pub fn new() -> Self {
Self {
schema: SCHEMA.to_string(),
parameters: None,
variables: None,
resources: Vec::new(),
metadata: None,
}
}
}

impl Resource {
#[must_use]
pub fn new() -> Self {
Self {
resource_type: String::new(),
name: String::new(),
depends_on: None,
properties: None,
}
}
}

impl Default for Resource {
fn default() -> Self {
Self::new()
}
}
27 changes: 27 additions & 0 deletions dsc_lib/src/configure/config_result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use crate::dscresources::invoke_result::{GetResult, SetResult, TestResult};
use crate::configure::config_doc;

#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
pub enum MessageLevel {
Expand Down Expand Up @@ -126,3 +127,29 @@ impl Default for ConfigurationTestResult {
Self::new()
}
}

#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct ConfigurationExportResult {
pub result: Option<config_doc::Configuration>,
pub messages: Vec<ResourceMessage>,
#[serde(rename = "hadErrors")]
pub had_errors: bool,
}

impl ConfigurationExportResult {
#[must_use]
pub fn new() -> Self {
Self {
result: None,
messages: Vec::new(),
had_errors: false,
}
}
}

impl Default for ConfigurationExportResult {
fn default() -> Self {
Self::new()
}
}
Loading

0 comments on commit e1b30f9

Please sign in to comment.