Skip to content

Commit 9110c74

Browse files
authored
Merge pull request #1124 from SteveL-MSFT/adapter-single
Add new inputKind for adapters to work against single resource instead of config
2 parents f3d1b42 + 7b71050 commit 9110c74

File tree

13 files changed

+656
-147
lines changed

13 files changed

+656
-147
lines changed

.vscode/launch.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@
2020
"type": "lldb",
2121
"request": "attach",
2222
"pid": "${command:pickMyProcess}",
23+
"expressions": "simple",
24+
"preRunCommands": [
25+
// !! change this path if you placed the script somewhere else !!
26+
"command script import ~/.vscode/rust_prettifier_for_lldb.py"
27+
],
2328
},
2429
{
2530
"name": "(Windows) Attach",

dsc/tests/dsc_adapter.tests.ps1

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# Copyright (c) Microsoft Corporation.
2+
# Licensed under the MIT License.
3+
4+
Describe 'Tests for adapter support' {
5+
Context 'Adapter support single resource' {
6+
It 'Direct resource invocation for: <operation>' -TestCases @(
7+
@{ operation = 'get' },
8+
@{ operation = 'set' },
9+
@{ operation = 'test' },
10+
@{ operation = 'export' }
11+
){
12+
param($operation)
13+
14+
$out = dsc resource $operation -r Adapted/One -i '{"one":"1"}' 2>$TestDrive/error.log | ConvertFrom-Json -Depth 10
15+
$LASTEXITCODE | Should -Be 0 -Because (Get-Content $TestDrive/error.log | Out-String)
16+
switch ($operation) {
17+
'get' {
18+
$out.actualState.one | Should -BeExactly 'value1'
19+
}
20+
'set' {
21+
$out.afterState.one | Should -BeExactly 'value1'
22+
}
23+
'test' {
24+
$out.actualState.one | Should -BeExactly 'value1'
25+
$out.inDesiredState | Should -BeFalse
26+
$out.differingProperties | Should -Be @('one')
27+
}
28+
'export' {
29+
$out.resources.count | Should -Be 2
30+
$out.resources[0].type | Should -BeExactly 'Adapted/One'
31+
$out.resources[0].name | Should -BeExactly 'first'
32+
$out.resources[0].properties.one | Should -BeExactly 'first1'
33+
$out.resources[1].type | Should -BeExactly 'Adapted/One'
34+
$out.resources[1].name | Should -BeExactly 'second'
35+
$out.resources[1].properties.one | Should -BeExactly 'second1'
36+
}
37+
}
38+
}
39+
40+
It 'Config resource invocation for: <operation>' -TestCases @(
41+
@{ operation = 'get' },
42+
@{ operation = 'set' },
43+
@{ operation = 'test' },
44+
@{ operation = 'export' }
45+
){
46+
param($operation)
47+
48+
$config_yaml = @"
49+
`$schema: https://aka.ms/dsc/schemas/v3/bundled/config/document.json
50+
resources:
51+
- name: Test
52+
type: Adapted/Two
53+
properties:
54+
two: '2'
55+
"@
56+
$out = dsc config $operation -i $config_yaml 2>$TestDrive/error.log | ConvertFrom-Json -Depth 10
57+
$LASTEXITCODE | Should -Be 0 -Because (Get-Content $TestDrive/error.log | Out-String)
58+
switch ($operation) {
59+
'get' {
60+
$out.results.Count | Should -Be 1
61+
$out.results[0].Name | Should -Be 'Test'
62+
$out.results[0].type | Should -BeExactly 'Adapted/Two'
63+
$out.results[0].result.actualState.two | Should -BeExactly 'value2' -Because ($out | ConvertTo-Json -Depth 10 | Out-String)
64+
}
65+
'set' {
66+
$out.results.Count | Should -Be 1
67+
$out.results[0].Name | Should -Be 'Test'
68+
$out.results[0].type | Should -BeExactly 'Adapted/Two'
69+
$out.results[0].result.afterState.two | Should -BeExactly 'value2' -Because ($out | ConvertTo-Json -Depth 10 | Out-String)
70+
}
71+
'test' {
72+
$out.results.Count | Should -Be 1
73+
$out.results[0].Name | Should -Be 'Test'
74+
$out.results[0].type | Should -BeExactly 'Adapted/Two'
75+
$out.results[0].result.actualState.two | Should -BeExactly 'value2' -Because ($out | ConvertTo-Json -Depth 10 | Out-String)
76+
$out.results[0].result.inDesiredState | Should -BeFalse
77+
$out.results[0].result.differingProperties | Should -Be @('two') -Because ($out | ConvertTo-Json -Depth 10 | Out-String)
78+
}
79+
'export' {
80+
$out.resources.Count | Should -Be 2
81+
$out.resources[0].Name | Should -Be 'first'
82+
$out.resources[0].type | Should -BeExactly 'Adapted/Two'
83+
$out.resources[0].properties.two | Should -BeExactly 'first2'
84+
$out.resources[1].Name | Should -Be 'second'
85+
$out.resources[1].type | Should -BeExactly 'Adapted/Two'
86+
$out.resources[1].properties.two | Should -BeExactly 'second2'
87+
}
88+
}
89+
}
90+
}
91+
}

dsc_lib/locales/en-us.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,9 @@ resourceImplementsValidate = "Resource implements validation"
196196
resourceValidationFailed = "Resource failed validation"
197197
validatingSchema = "Validating against schema"
198198
validationFailed = "Failed validation"
199+
adapterResourceNotFound = "Adapter resource '%{adapter}' not found"
200+
adapterManifestNotFound = "Adapter manifest for '%{adapter}' not found"
201+
adapterDoesNotSupportDelete = "Adapter '%{adapter}' does not support delete operation"
199202

200203
[dscresources.resource_manifest]
201204
resourceManifestSchemaTitle = "Resource manifest schema URI"

dsc_lib/src/configure/mod.rs

Lines changed: 26 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,10 @@ use crate::configure::context::{Context, ProcessMode};
66
use crate::configure::{config_doc::RestartRequired, parameters::Input};
77
use crate::discovery::discovery_trait::DiscoveryFilter;
88
use crate::dscerror::DscError;
9-
use crate::dscresources::invoke_result::ExportResult;
109
use crate::dscresources::{
11-
{dscresource::{Capability, Invoke, get_diff, validate_properties},
12-
invoke_result::{GetResult, SetResult, TestResult, ResourceSetResponse}},
13-
resource_manifest::Kind,
10+
{dscresource::{Capability, Invoke, get_diff, validate_properties, get_adapter_input_kind},
11+
invoke_result::{GetResult, SetResult, TestResult, ExportResult, ResourceSetResponse}},
12+
resource_manifest::{AdapterInputKind, Kind},
1413
};
1514
use crate::DscResource;
1615
use crate::discovery::Discovery;
@@ -177,7 +176,7 @@ fn escape_property_values(properties: &Map<String, Value>) -> Result<Option<Map<
177176
}
178177

179178
fn add_metadata(dsc_resource: &DscResource, mut properties: Option<Map<String, Value>>, resource_metadata: Option<Metadata> ) -> Result<String, DscError> {
180-
if dsc_resource.kind == Kind::Adapter {
179+
if dsc_resource.kind == Kind::Adapter && get_adapter_input_kind(dsc_resource)? == AdapterInputKind::Full {
181180
// add metadata to the properties so the adapter knows this is a config
182181
let mut metadata: Map<String, Value> = Map::new();
183182
if let Some(resource_metadata) = resource_metadata {
@@ -319,6 +318,15 @@ impl Configurator {
319318
&self.config
320319
}
321320

321+
/// Get the discovery.
322+
///
323+
/// # Returns
324+
///
325+
/// * `&Discovery` - The discovery.
326+
pub fn discovery(&mut self) -> &mut Discovery {
327+
&mut self.discovery
328+
}
329+
322330
fn get_properties(&mut self, resource: &Resource, resource_kind: &Kind) -> Result<Option<Map<String, Value>>, DscError> {
323331
match resource_kind {
324332
Kind::Group => {
@@ -342,14 +350,14 @@ impl Configurator {
342350
/// This function will return an error if the underlying resource fails.
343351
pub fn invoke_get(&mut self) -> Result<ConfigurationGetResult, DscError> {
344352
self.unroll_copy_loops()?;
345-
353+
346354
let mut result = ConfigurationGetResult::new();
347355
let resources = get_resource_invocation_order(&self.config, &mut self.statement_parser, &self.context)?;
348356
let mut progress = ProgressBar::new(resources.len() as u64, self.progress_format)?;
349357
let discovery = &mut self.discovery.clone();
350358
for resource in resources {
351359
let evaluated_name = self.evaluate_resource_name(&resource.name)?;
352-
360+
353361
progress.set_resource(&evaluated_name, &resource.resource_type);
354362
progress.write_activity(format!("Get '{evaluated_name}'").as_str());
355363
if self.skip_resource(&resource)? {
@@ -424,14 +432,14 @@ impl Configurator {
424432
#[allow(clippy::too_many_lines)]
425433
pub fn invoke_set(&mut self, skip_test: bool) -> Result<ConfigurationSetResult, DscError> {
426434
self.unroll_copy_loops()?;
427-
435+
428436
let mut result = ConfigurationSetResult::new();
429437
let resources = get_resource_invocation_order(&self.config, &mut self.statement_parser, &self.context)?;
430438
let mut progress = ProgressBar::new(resources.len() as u64, self.progress_format)?;
431439
let discovery = &mut self.discovery.clone();
432440
for resource in resources {
433441
let evaluated_name = self.evaluate_resource_name(&resource.name)?;
434-
442+
435443
progress.set_resource(&evaluated_name, &resource.resource_type);
436444
progress.write_activity(format!("Set '{evaluated_name}'").as_str());
437445
if self.skip_resource(&resource)? {
@@ -580,14 +588,14 @@ impl Configurator {
580588
/// This function will return an error if the underlying resource fails.
581589
pub fn invoke_test(&mut self) -> Result<ConfigurationTestResult, DscError> {
582590
self.unroll_copy_loops()?;
583-
591+
584592
let mut result = ConfigurationTestResult::new();
585593
let resources = get_resource_invocation_order(&self.config, &mut self.statement_parser, &self.context)?;
586594
let mut progress = ProgressBar::new(resources.len() as u64, self.progress_format)?;
587595
let discovery = &mut self.discovery.clone();
588596
for resource in resources {
589597
let evaluated_name = self.evaluate_resource_name(&resource.name)?;
590-
598+
591599
progress.set_resource(&evaluated_name, &resource.resource_type);
592600
progress.write_activity(format!("Test '{evaluated_name}'").as_str());
593601
if self.skip_resource(&resource)? {
@@ -658,7 +666,7 @@ impl Configurator {
658666
/// This function will return an error if the underlying resource fails.
659667
pub fn invoke_export(&mut self) -> Result<ConfigurationExportResult, DscError> {
660668
self.unroll_copy_loops()?;
661-
669+
662670
let mut result = ConfigurationExportResult::new();
663671
let mut conf = config_doc::Configuration::new();
664672
conf.metadata.clone_from(&self.config.metadata);
@@ -668,7 +676,7 @@ impl Configurator {
668676
let discovery = &mut self.discovery.clone();
669677
for resource in &resources {
670678
let evaluated_name = self.evaluate_resource_name(&resource.name)?;
671-
679+
672680
progress.set_resource(&evaluated_name, &resource.resource_type);
673681
progress.write_activity(format!("Export '{evaluated_name}'").as_str());
674682
if self.skip_resource(resource)? {
@@ -916,7 +924,7 @@ impl Configurator {
916924
fn unroll_copy_loops(&mut self) -> Result<(), DscError> {
917925
let mut config = self.config.clone();
918926
let config_copy = config.clone();
919-
927+
920928
for resource in config_copy.resources {
921929
// if the resource contains `Copy`, unroll it
922930
if let Some(copy) = &resource.copy {
@@ -931,7 +939,7 @@ impl Configurator {
931939
return Err(DscError::Parser(t!("configure.mod.copyNameResultNotString").to_string()))
932940
};
933941
new_resource.name = new_name.to_string();
934-
942+
935943
new_resource.copy = None;
936944
copy_resources.push(new_resource);
937945
}
@@ -941,7 +949,7 @@ impl Configurator {
941949
config.resources.extend(copy_resources);
942950
}
943951
}
944-
952+
945953
self.config = config;
946954
Ok(())
947955
}
@@ -967,12 +975,12 @@ impl Configurator {
967975
if self.context.process_mode == ProcessMode::Copy {
968976
return Ok(name.to_string());
969977
}
970-
978+
971979
// evaluate the resource name (handles both expressions and literals)
972980
let Value::String(evaluated_name) = self.statement_parser.parse_and_execute(name, &self.context)? else {
973981
return Err(DscError::Parser(t!("configure.mod.nameResultNotString").to_string()))
974982
};
975-
983+
976984
Ok(evaluated_name)
977985
}
978986

0 commit comments

Comments
 (0)