Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable using adapter resources without the adapter wrapper #720

Merged
merged 10 commits into from
Apr 1, 2025
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
11 changes: 7 additions & 4 deletions dsc/examples/powershell.dsc.yaml
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
# Example configuration mixing native app resources with classic PS resources
$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
metadata:
Microsoft.DSC:
securityContext: elevated
resources:
- name: Use class PowerShell resources
type: Microsoft.DSC/PowerShell
type: Microsoft.Windows/WindowsPowerShell
properties:
resources:
- name: OpenSSH service
type: PsDesiredStateConfiguration/MSFT_ServiceResource
type: PsDesiredStateConfiguration/Service
properties:
Name: sshd
- name: Administrator
type: PsDesiredStateConfiguration/MSFT_UserResource
type: PsDesiredStateConfiguration/User
properties:
UserName: administrator
- name: current user registry
type: Microsoft.Windows/Registry
properties:
keyPath: HKLM\Software\Microsoft\Windows NT\CurrentVersion
valueName: ProductName
_ensure: Present
_exist: True
1 change: 0 additions & 1 deletion dsc/locales/en-us.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ noParameters = "No parameters specified"
[resource_command]
implementedAs = "implemented as"
invalidOperationOnAdapter = "Can not perform this operation on the adapter itself"
adapterNotFound = "Adapter not found"
setInputEmpty = "Desired input is empty"
testInputEmpty = "Expected input is required"

Expand Down
96 changes: 17 additions & 79 deletions dsc/src/resource_command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
// Licensed under the MIT License.

use crate::args::OutputFormat;
use crate::util::{EXIT_DSC_ERROR, EXIT_INVALID_ARGS, EXIT_JSON_ERROR, EXIT_DSC_RESOURCE_NOT_FOUND, add_type_name_to_json, write_object};
use crate::util::{EXIT_DSC_ERROR, EXIT_INVALID_ARGS, EXIT_JSON_ERROR, EXIT_DSC_RESOURCE_NOT_FOUND, write_object};
use dsc_lib::configure::config_doc::{Configuration, ExecutionKind};
use dsc_lib::configure::add_resource_export_results_to_configuration;
use dsc_lib::dscresources::{resource_manifest::Kind, invoke_result::{GetResult, ResourceGetResponse}};
Expand All @@ -16,8 +16,8 @@ use dsc_lib::{
};
use std::process::exit;

pub fn get(dsc: &DscManager, resource_type: &str, mut input: String, format: Option<&OutputFormat>) {
let Some(mut resource) = get_resource(dsc, resource_type) else {
pub fn get(dsc: &DscManager, resource_type: &str, input: &str, format: Option<&OutputFormat>) {
let Some(resource) = get_resource(dsc, resource_type) else {
error!("{}", DscError::ResourceNotFound(resource_type.to_string()).to_string());
exit(EXIT_DSC_RESOURCE_NOT_FOUND);
};
Expand All @@ -28,17 +28,7 @@ pub fn get(dsc: &DscManager, resource_type: &str, mut input: String, format: Opt
exit(EXIT_DSC_ERROR);
}

if let Some(requires) = &resource.require_adapter {
input = add_type_name_to_json(input, resource.type_name.clone());
if let Some(pr) = get_resource(dsc, requires) {
resource = pr;
} else {
error!("{}: {requires}", t!("resource_command.adapterNotFound"));
return;
};
}

match resource.get(input.as_str()) {
match resource.get(input) {
Ok(result) => {
// convert to json
let json = match serde_json::to_string(&result) {
Expand All @@ -58,8 +48,8 @@ pub fn get(dsc: &DscManager, resource_type: &str, mut input: String, format: Opt
}

pub fn get_all(dsc: &DscManager, resource_type: &str, format: Option<&OutputFormat>) {
let mut input = String::new();
let Some(mut resource) = get_resource(dsc, resource_type) else {
let input = String::new();
let Some(resource) = get_resource(dsc, resource_type) else {
error!("{}", DscError::ResourceNotFound(resource_type.to_string()).to_string());
exit(EXIT_DSC_RESOURCE_NOT_FOUND);
};
Expand All @@ -70,16 +60,6 @@ pub fn get_all(dsc: &DscManager, resource_type: &str, format: Option<&OutputForm
exit(EXIT_DSC_ERROR);
}

if let Some(requires) = &resource.require_adapter {
input = add_type_name_to_json(input, resource.type_name.clone());
if let Some(pr) = get_resource(dsc, requires) {
resource = pr;
} else {
error!("{}: {requires}", t!("resource_command.adapterNotFound"));
return;
};
}

let export_result = match resource.export(&input) {
Ok(export) => { export }
Err(err) => {
Expand Down Expand Up @@ -107,13 +87,13 @@ pub fn get_all(dsc: &DscManager, resource_type: &str, format: Option<&OutputForm
}
}

pub fn set(dsc: &DscManager, resource_type: &str, mut input: String, format: Option<&OutputFormat>) {
pub fn set(dsc: &DscManager, resource_type: &str, input: &str, format: Option<&OutputFormat>) {
if input.is_empty() {
error!("{}", t!("resource_command.setInputEmpty"));
exit(EXIT_INVALID_ARGS);
}

let Some(mut resource) = get_resource(dsc, resource_type) else {
let Some(resource) = get_resource(dsc, resource_type) else {
error!("{}", DscError::ResourceNotFound(resource_type.to_string()).to_string());
exit(EXIT_DSC_RESOURCE_NOT_FOUND);
};
Expand All @@ -124,17 +104,7 @@ pub fn set(dsc: &DscManager, resource_type: &str, mut input: String, format: Opt
exit(EXIT_DSC_ERROR);
}

if let Some(requires) = &resource.require_adapter {
input = add_type_name_to_json(input, resource.type_name.clone());
if let Some(pr) = get_resource(dsc, requires) {
resource = pr;
} else {
error!("{}: {requires}", t!("resource_command.adapterNotFound"));
return;
};
}

match resource.set(input.as_str(), true, &ExecutionKind::Actual) {
match resource.set(input, true, &ExecutionKind::Actual) {
Ok(result) => {
// convert to json
let json = match serde_json::to_string(&result) {
Expand All @@ -153,13 +123,13 @@ pub fn set(dsc: &DscManager, resource_type: &str, mut input: String, format: Opt
}
}

pub fn test(dsc: &DscManager, resource_type: &str, mut input: String, format: Option<&OutputFormat>) {
pub fn test(dsc: &DscManager, resource_type: &str, input: &str, format: Option<&OutputFormat>) {
if input.is_empty() {
error!("{}", t!("resource_command.testInputEmpty"));
exit(EXIT_INVALID_ARGS);
}

let Some(mut resource) = get_resource(dsc, resource_type) else {
let Some(resource) = get_resource(dsc, resource_type) else {
error!("{}", DscError::ResourceNotFound(resource_type.to_string()).to_string());
exit(EXIT_DSC_RESOURCE_NOT_FOUND);
};
Expand All @@ -170,17 +140,7 @@ pub fn test(dsc: &DscManager, resource_type: &str, mut input: String, format: Op
exit(EXIT_DSC_ERROR);
}

if let Some(requires) = &resource.require_adapter {
input = add_type_name_to_json(input, resource.type_name.clone());
if let Some(pr) = get_resource(dsc, requires) {
resource = pr;
} else {
error!("{}: {requires}", t!("resource_command.adapterNotFound"));
return;
};
}

match resource.test(input.as_str()) {
match resource.test(input) {
Ok(result) => {
// convert to json
let json = match serde_json::to_string(&result) {
Expand All @@ -199,8 +159,8 @@ pub fn test(dsc: &DscManager, resource_type: &str, mut input: String, format: Op
}
}

pub fn delete(dsc: &DscManager, resource_type: &str, mut input: String) {
let Some(mut resource) = get_resource(dsc, resource_type) else {
pub fn delete(dsc: &DscManager, resource_type: &str, input: &str) {
let Some(resource) = get_resource(dsc, resource_type) else {
error!("{}", DscError::ResourceNotFound(resource_type.to_string()).to_string());
exit(EXIT_DSC_RESOURCE_NOT_FOUND);
};
Expand All @@ -211,17 +171,7 @@ pub fn delete(dsc: &DscManager, resource_type: &str, mut input: String) {
exit(EXIT_DSC_ERROR);
}

if let Some(requires) = &resource.require_adapter {
input = add_type_name_to_json(input, resource.type_name.clone());
if let Some(pr) = get_resource(dsc, requires) {
resource = pr;
} else {
error!("{}: {requires}", t!("resource_command.adapterNotFound"));
return;
};
}

match resource.delete(input.as_str()) {
match resource.delete(input) {
Ok(()) => {}
Err(err) => {
error!("Error: {err}");
Expand Down Expand Up @@ -259,7 +209,7 @@ pub fn schema(dsc: &DscManager, resource_type: &str, format: Option<&OutputForma
}
}

pub fn export(dsc: &mut DscManager, resource_type: &str, mut input: String, format: Option<&OutputFormat>) {
pub fn export(dsc: &mut DscManager, resource_type: &str, input: &str, format: Option<&OutputFormat>) {
let Some(dsc_resource) = get_resource(dsc, resource_type) else {
error!("{}", DscError::ResourceNotFound(resource_type.to_string()).to_string());
exit(EXIT_DSC_RESOURCE_NOT_FOUND);
Expand All @@ -270,20 +220,8 @@ pub fn export(dsc: &mut DscManager, resource_type: &str, mut input: String, form
exit(EXIT_DSC_ERROR);
}

let mut adapter_resource: Option<&DscResource> = None;
if let Some(requires) = &dsc_resource.require_adapter {
input = add_type_name_to_json(input, dsc_resource.type_name.clone());
if let Some(pr) = get_resource(dsc, requires) {
adapter_resource = Some(pr);
} else {
error!("{}: {requires}", t!("resource_command.adapterNotFound"));
return;
};
}

let mut conf = Configuration::new();

if let Err(err) = add_resource_export_results_to_configuration(dsc_resource, adapter_resource, &mut conf, &input) {
if let Err(err) = add_resource_export_results_to_configuration(dsc_resource, &mut conf, input) {
error!("{err}");
exit(EXIT_DSC_ERROR);
}
Expand Down
10 changes: 5 additions & 5 deletions dsc/src/subcommand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -548,30 +548,30 @@ pub fn resource(subcommand: &ResourceSubCommand, progress_format: ProgressFormat
ResourceSubCommand::Export { resource, input, file, output_format } => {
dsc.find_resources(&[resource.to_string()], progress_format);
let parsed_input = get_input(input.as_ref(), file.as_ref());
resource_command::export(&mut dsc, resource, parsed_input, output_format.as_ref());
resource_command::export(&mut dsc, resource, &parsed_input, output_format.as_ref());
},
ResourceSubCommand::Get { resource, input, file: path, all, output_format } => {
dsc.find_resources(&[resource.to_string()], progress_format);
if *all { resource_command::get_all(&dsc, resource, output_format.as_ref()); }
else {
let parsed_input = get_input(input.as_ref(), path.as_ref());
resource_command::get(&dsc, resource, parsed_input, output_format.as_ref());
resource_command::get(&dsc, resource, &parsed_input, output_format.as_ref());
}
},
ResourceSubCommand::Set { resource, input, file: path, output_format } => {
dsc.find_resources(&[resource.to_string()], progress_format);
let parsed_input = get_input(input.as_ref(), path.as_ref());
resource_command::set(&dsc, resource, parsed_input, output_format.as_ref());
resource_command::set(&dsc, resource, &parsed_input, output_format.as_ref());
},
ResourceSubCommand::Test { resource, input, file: path, output_format } => {
dsc.find_resources(&[resource.to_string()], progress_format);
let parsed_input = get_input(input.as_ref(), path.as_ref());
resource_command::test(&dsc, resource, parsed_input, output_format.as_ref());
resource_command::test(&dsc, resource, &parsed_input, output_format.as_ref());
},
ResourceSubCommand::Delete { resource, input, file: path } => {
dsc.find_resources(&[resource.to_string()], progress_format);
let parsed_input = get_input(input.as_ref(), path.as_ref());
resource_command::delete(&dsc, resource, parsed_input);
resource_command::delete(&dsc, resource, &parsed_input);
},
}
}
Expand Down
31 changes: 0 additions & 31 deletions dsc/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,37 +129,6 @@ pub fn add_fields_to_json(json: &str, fields_to_add: &HashMap<String, String>) -
Ok(result)
}

/// Add the type property value to the JSON.
///
/// # Arguments
///
/// * `json` - The JSON to add the type property to
/// * `type_name` - The type name to add
///
/// # Returns
///
/// * `String` - The JSON with the type property added
#[must_use]
pub fn add_type_name_to_json(json: String, type_name: String) -> String
{
let mut map:HashMap<String,String> = HashMap::new();
map.insert(String::from("adapted_dsc_type"), type_name);

let mut j = json;
if j.is_empty()
{
j = String::from("{}");
}

match add_fields_to_json(&j, &map) {
Ok(json) => json,
Err(err) => {
error!("JSON: {err}");
exit(EXIT_JSON_ERROR);
}
}
}

/// Get the JSON schema for requested type.
///
/// # Arguments
Expand Down
10 changes: 8 additions & 2 deletions dsc_lib/locales/en-us.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ circularDependency = "Circular dependency detected for resource named '%{resourc
invocationOrder = "Resource invocation order"

[configure.mod]
escapePropertyValues = "Escape returned property values"
nestedArraysNotSupported = "Nested arrays not supported"
arrayElementCouldNotTransformAsString = "Array element could not be transformed as string"
valueCouldNotBeTransformedAsString = "Property value '%{value}' could not be transformed as string"
Expand Down Expand Up @@ -89,7 +88,7 @@ invokeGet = "Invoking get for '%{resource}'"
invokeGetUsing = "Invoking get '%{resource}' using '%{executable}'"
verifyOutputUsing = "Verifying output of get '%{resource}' using '%{executable}'"
groupGetResponse = "Group get response: %{response}"
failedParseJson = "Failed to parse JSON from get %{executable}|%{stdout}|%{stderr} -> %{err}"
failedParseJson = "Failed to parse JSON from 'get': executable = '%{executable}' stdout = '%{stdout}' stderr = '%{stderr}' -> %{err}"
invokeSet = "Invoking set for '%{resource}'"
noPretest = "No pretest, invoking test on '%{resource}'"
syntheticWhatIf = "cannot process what-if execution type, as resource implements pre-test and does not support what-if"
Expand Down Expand Up @@ -139,9 +138,16 @@ invokeSet = "Invoking set for '%{resource}'"
invokeTest = "Invoking test for '%{resource}'"
invokeDelete = "Invoking delete for '%{resource}'"
invokeValidate = "Invoking validate for '%{resource}'"
invokeValidateNotSupported = "Invoking validate is not supported for adapted resource '%{resource}'"
invokeSchema = "Invoking schema for '%{resource}'"
invokeSchemaNotSupported = "Invoking schema is not supported for adapted resource '%{resource}'"
invokeExport = "Invoking export for '%{resource}'"
invokeExportReturnedNoResult = "Invoking export returned no result for '%{resource}'"
invokeResolve = "Invoking resolve for '%{resource}'"
invokeResolveNotSupported = "Invoking resolve is not supported for adapted resource '%{resource}'"
invokeReturnedWrongResult = "Invoking '%{operation}' on '%{resource}' returned unexpected result"
propertyIncorrectType = "Property '%{property}' is not of type '%{property_type}'"
propertyNotFound = "Property '%{property}' not found"
subDiff = "diff: sub diff for '%{key}'"
diffArray = "diff: arrays differ for '%{key}'"
diffNotArray = "diff: '%{key}' is not an array"
Expand Down
10 changes: 0 additions & 10 deletions dsc_lib/src/configure/config_doc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,6 @@ use std::collections::HashMap;

use crate::{dscerror::DscError, schemas::DscRepoSchema};

#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub enum ContextKind {
Configuration,
Resource,
}

#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
#[serde(rename_all = "camelCase")]
pub enum SecurityContextKind {
Expand Down Expand Up @@ -63,9 +56,6 @@ pub struct MicrosoftDscMetadata {
/// The security context of the configuration operation, can be specified to be required
#[serde(rename = "securityContext", skip_serializing_if = "Option::is_none")]
pub security_context: Option<SecurityContextKind>,
/// Identifies if the operation is part of a configuration
#[serde(skip_serializing_if = "Option::is_none")]
pub context: Option<ContextKind>,
}

#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, JsonSchema)]
Expand Down
Loading