Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion dsc/assertion.dsc.resource.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"test"
],
"input": "stdin",
"preTest": true,
"implementsPretest": true,
"return": "state"
},
"test": {
Expand Down
2 changes: 1 addition & 1 deletion dsc/group.dsc.resource.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
"set"
],
"input": "stdin",
"preTest": true,
"implementsPretest": true,
"return": "state"
},
"test": {
Expand Down
2 changes: 1 addition & 1 deletion dsc/parallel.dsc.resource.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"set"
],
"input": "stdin",
"preTest": true,
"implementsPretest": true,
"return": "state"
},
"test": {
Expand Down
13 changes: 9 additions & 4 deletions dsc/src/resource_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ pub fn get(dsc: &mut DscManager, resource: &str, input: &Option<String>, stdin:
//TODO: add to debug stream: println!("handle_resource_get - {} implemented_as - {:?}", resource.type_name, resource.implemented_as);
if let Some(requires) = resource.requires {
input = add_type_name_to_json(input, resource.type_name);
resource = get_resource(dsc, &requires.clone());
resource = get_resource(dsc, &requires);
}

//TODO: add to debug stream: println!("handle_resource_get - input - {}", input);
Expand Down Expand Up @@ -74,18 +74,23 @@ pub fn get_all(dsc: &mut DscManager, resource: &str, _input: &Option<String>, _s

pub fn set(dsc: &mut DscManager, resource: &str, input: &Option<String>, stdin: &Option<String>, format: &Option<OutputFormat>) {
let mut input = get_input(input, stdin);
if input.is_empty() {
eprintln!("Error: Input is empty");
exit(EXIT_INVALID_ARGS);
}

let mut resource = get_resource(dsc, resource);

//TODO: add to debug stream: println!("handle_resource_set - {} implemented_as - {:?}", resource.type_name, resource.implemented_as);

if let Some(requires) = resource.requires {
input = add_type_name_to_json(input, resource.type_name);
resource = get_resource(dsc, &requires.clone());
resource = get_resource(dsc, &requires);
}

//TODO: add to debug stream: println!("handle_resource_get - input - {}", input);

match resource.set(input.as_str()) {
match resource.set(input.as_str(), true) {
Ok(result) => {
// convert to json
let json = match serde_json::to_string(&result) {
Expand All @@ -112,7 +117,7 @@ pub fn test(dsc: &mut DscManager, resource: &str, input: &Option<String>, stdin:

if let Some(requires) = resource.requires {
input = add_type_name_to_json(input, resource.type_name);
resource = get_resource(dsc, &requires.clone());
resource = get_resource(dsc, &requires);
}

//TODO: add to debug stream: println!("handle_resource_test - input - {}", input);
Expand Down
2 changes: 1 addition & 1 deletion dsc/src/subcommand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ pub fn config_get(configurator: &Configurator, format: &Option<OutputFormat>)

pub fn config_set(configurator: &Configurator, format: &Option<OutputFormat>)
{
match configurator.invoke_set(ErrorAction::Continue, || { /* code */ }) {
match configurator.invoke_set(false, ErrorAction::Continue, || { /* code */ }) {
Ok(result) => {
let json = match serde_json::to_string(&result) {
Ok(json) => json,
Expand Down
90 changes: 78 additions & 12 deletions dsc/tests/dsc_set.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,27 @@

Describe 'config set tests' {
BeforeEach {
$json = @'
{
"keyPath": "HKCU\\1\\2\\3",
"_ensure": "Absent"
}
if ($IsWindows) {
$json = @'
{
"keyPath": "HKCU\\1\\2\\3",
"_ensure": "Absent"
}
'@
$json | registry config set
$json | registry config set
}
}

AfterEach {
$json = @'
{
"keyPath": "HKCU\\1",
"_ensure": "Absent"
}
if ($IsWindows) {
$json = @'
{
"keyPath": "HKCU\\1",
"_ensure": "Absent"
}
'@
$json | registry config set
$json | registry config set
}
}

It 'can set and remove a registry value' -Skip:(!$IsWindows) {
Expand Down Expand Up @@ -62,4 +66,66 @@ Describe 'config set tests' {
$result.changedProperties | Should -Be @('keyPath')
($result.psobject.properties | Measure-Object).Count | Should -Be 3
}

It 'set can be used on a resource that does not implement test' {
$manifest = @'
{
"manifestVersion": "1.0.0",
"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
$oldPath = $env:DSC_RESOURCE_PATH
try {
$env:DSC_RESOURCE_PATH = $TestDrive
$out = '{ "test": true }' | dsc resource set -r Test/SetNoTest | ConvertFrom-Json
$LASTEXITCODE | Should -Be 0
$out.BeforeState.test | Should -Be $true
$out.AfterState.test | Should -Be $false
}
finally {
$env:DSC_RESOURCE_PATH = $oldPath
}
}
}
22 changes: 11 additions & 11 deletions dsc_lib/src/configure/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@ pub enum ErrorAction {
}

/// Add the results of an export operation to a configuration.
///
///
/// # Arguments
///
///
/// * `resource` - The resource to export.
/// * `conf` - The configuration to add the results to.
///
/// # Errors
///
///
/// This function will return an error if the underlying resource fails.
pub fn add_resource_export_results_to_configuration(resource: &DscResource, conf: &mut Configuration) -> Result<(), DscError> {
let export_result = resource.export()?;
Expand Down Expand Up @@ -119,7 +119,7 @@ impl Configurator {
/// # Errors
///
/// This function will return an error if the underlying resource fails.
pub fn invoke_set(&self, _error_action: ErrorAction, _progress_callback: impl Fn() + 'static) -> Result<ConfigurationSetResult, DscError> {
pub fn invoke_set(&self, skip_test: bool, _error_action: ErrorAction, _progress_callback: impl Fn() + 'static) -> Result<ConfigurationSetResult, DscError> {
let (config, messages, had_errors) = self.validate_config()?;
let mut result = ConfigurationSetResult::new();
result.messages = messages;
Expand All @@ -133,7 +133,7 @@ impl Configurator {
};
//TODO: add to debug stream:println!("{}", &resource.resource_type);
let desired = serde_json::to_string(&resource.properties)?;
let set_result = dsc_resource.set(&desired)?;
let set_result = dsc_resource.set(&desired, skip_test)?;
let resource_result = config_result::ResourceSetResult {
name: resource.name.clone(),
resource_type: resource.resource_type.clone(),
Expand Down Expand Up @@ -203,18 +203,18 @@ impl Configurator {
}

/// Invoke the export operation on a configuration.
///
///
/// # Arguments
///
///
/// * `error_action` - The error action to use.
/// * `progress_callback` - A callback to call when progress is made.
///
///
/// # Returns
///
///
/// * `ConfigurationExportResult` - The result of the export operation.
///
///
/// # Errors
///
///
/// This function will return an error if the underlying resource fails.
pub fn invoke_export(&self, _error_action: ErrorAction, _progress_callback: impl Fn() + 'static) -> Result<ConfigurationExportResult, DscError> {
let (config, messages, had_errors) = self.validate_config()?;
Expand Down
29 changes: 15 additions & 14 deletions dsc_lib/src/dscresources/command_resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,17 +49,18 @@ pub fn invoke_get(resource: &ResourceManifest, cwd: &str, filter: &str) -> Resul
///
/// * `resource` - The resource manifest
/// * `desired` - The desired state of the resource in JSON
/// * `skip_test` - If true, skip the test and directly invoke the set operation
///
/// # Errors
///
/// Error returned if the resource does not successfully set the desired state
pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str) -> Result<SetResult, DscError> {
pub fn invoke_set(resource: &ResourceManifest, cwd: &str, desired: &str, skip_test: bool) -> Result<SetResult, DscError> {
let Some(set) = resource.set.as_ref() else {
return Err(DscError::NotImplemented("set".to_string()));
};
verify_json(resource, cwd, desired)?;
// if resource doesn't implement a pre-test, we execute test first to see if a set is needed
if !set.pre_test.unwrap_or_default() {
if !skip_test && !set.pre_test.unwrap_or_default() {
let test_result = invoke_test(resource, cwd, desired)?;
if test_result.in_desired_state {
return Ok(SetResult {
Expand Down Expand Up @@ -201,19 +202,19 @@ pub fn invoke_test(resource: &ResourceManifest, cwd: &str, expected: &str) -> Re
}

/// Invoke the validate operation against a command resource.
///
///
/// # Arguments
///
///
/// * `resource` - The resource manifest for the command resource.
/// * `cwd` - The current working directory.
/// * `config` - The configuration to validate in JSON.
///
///
/// # Returns
///
///
/// * `ValidateResult` - The result of the validate operation.
///
///
/// # Errors
///
///
/// Error is returned if the underlying command returns a non-zero exit code.
pub fn invoke_validate(resource: &ResourceManifest, cwd: &str, config: &str) -> Result<ValidateResult, DscError> {
// TODO: use schema to validate config if validate is not implemented
Expand Down Expand Up @@ -271,18 +272,18 @@ pub fn get_schema(resource: &ResourceManifest, cwd: &str) -> Result<String, DscE
}

/// Invoke the export operation on a resource
///
///
/// # Arguments
///
///
/// * `resource` - The resource manifest
/// * `cwd` - The current working directory
///
///
/// # Returns
///
///
/// * `ExportResult` - The result of the export operation
///
///
/// # Errors
///
///
/// Error returned if the resource does not successfully export the current state
pub fn invoke_export(resource: &ResourceManifest, cwd: &str) -> Result<ExportResult, DscError> {

Expand Down
7 changes: 4 additions & 3 deletions dsc_lib/src/dscresources/dscresource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,11 +86,12 @@ pub trait Invoke {
/// # Arguments
///
/// * `desired` - The desired state as JSON to apply to the resource.
/// * `skip_test` - Whether to skip the test operation.
///
/// # Errors
///
/// This function will return an error if the underlying resource fails.
fn set(&self, desired: &str) -> Result<SetResult, DscError>;
fn set(&self, desired: &str, skip_test: bool) -> Result<SetResult, DscError>;

/// Invoke the test operation on the resource.
///
Expand Down Expand Up @@ -145,7 +146,7 @@ impl Invoke for DscResource {
}
}

fn set(&self, desired: &str) -> Result<SetResult, DscError> {
fn set(&self, desired: &str, skip_test: bool) -> Result<SetResult, DscError> {
match &self.implemented_as {
ImplementedAs::Custom(_custom) => {
Err(DscError::NotImplemented("set custom resources".to_string()))
Expand All @@ -155,7 +156,7 @@ impl Invoke for DscResource {
return Err(DscError::MissingManifest(self.type_name.clone()));
};
let resource_manifest = serde_json::from_value::<ResourceManifest>(manifest.clone())?;
command_resource::invoke_set(&resource_manifest, &self.directory, desired)
command_resource::invoke_set(&resource_manifest, &self.directory, desired, skip_test)
},
}
}
Expand Down
2 changes: 1 addition & 1 deletion dsc_lib/src/dscresources/resource_manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ pub struct SetMethod {
/// How to pass required input for a Set.
pub input: InputKind,
/// Whether to run the Test method before the Set method. True means the resource will perform its own test before running the Set method.
#[serde(rename = "preTest", skip_serializing_if = "Option::is_none")]
#[serde(rename = "implementsPretest", skip_serializing_if = "Option::is_none")]
pub pre_test: Option<bool>,
/// The type of return value expected from the Set method.
#[serde(rename = "return", skip_serializing_if = "Option::is_none")]
Expand Down
Loading