Skip to content
Open
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
1 change: 1 addition & 0 deletions build.data.json
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,7 @@
"TestOnly": true,
"CopyFiles": {
"All": [
"adaptedTest.dsc.adaptedResource.json",
"dsctest.dsc.manifests.json"
]
}
Expand Down
20 changes: 5 additions & 15 deletions dsc/src/subcommand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ use dsc_lib::{
ValidateResult,
},
dscresources::dscresource::{Capability, ImplementedAs, validate_json, validate_properties},
dscresources::resource_manifest::import_manifest,
extensions::dscextension::Capability as ExtensionCapability,
functions::FunctionDispatcher,
progress::ProgressFormat,
Expand Down Expand Up @@ -513,7 +512,7 @@ pub fn validate_config(config: &Configuration, progress_format: ProgressFormat)
};

// see if the resource is command based
if resource.implemented_as == ImplementedAs::Command {
if resource.implemented_as == Some(ImplementedAs::Command) {
validate_properties(resource, &resource_block["properties"])?;
}
}
Expand Down Expand Up @@ -799,28 +798,19 @@ pub fn list_resources(dsc: &mut DscManager, resource_name: Option<&String>, adap
}

// if description, tags, or write_table is specified, pull resource manifest if it exists
if let Some(ref resource_manifest) = resource.manifest {
let manifest = match import_manifest(resource_manifest.clone()) {
Ok(resource_manifest) => resource_manifest,
Err(err) => {
error!("{} {}: {err}", t!("subcommand.invalidManifest"), resource.type_name);
continue;
}
};

if let Some(ref manifest) = resource.manifest {
// if description is specified, skip if resource description does not contain it
if description.is_some() &&
(manifest.description.is_none() | !manifest.description.unwrap_or_default().to_lowercase().contains(&description.unwrap_or(&String::new()).to_lowercase())) {
if description.is_some() && (manifest.description.is_none() | !manifest.description.clone().unwrap_or_default().to_lowercase().contains(&description.unwrap_or(&String::new()).to_lowercase())) {
continue;
}

// if tags is specified, skip if resource tags do not contain the tags
if let Some(tags) = tags {
let Some(manifest_tags) = manifest.tags else { continue; };
let Some(manifest_tags) = &manifest.tags else { continue; };

let mut found = false;
for tag_to_find in tags {
for tag in &manifest_tags {
for tag in manifest_tags {
if tag.to_lowercase() == tag_to_find.to_lowercase() {
found = true;
break;
Expand Down
78 changes: 78 additions & 0 deletions dsc/tests/dsc_adapter.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -150,4 +150,82 @@ Describe 'Tests for adapter support' {
"$TestDrive/error.log" | Should -FileContentMatch "Invoking get for 'Microsoft.Adapter/PowerShell'" -Because (Get-Content $TestDrive/error.log | Out-String)
}
}

Context 'Adapted resource manifests' {
It 'Adapted resource are found in individual manifest' {
$out = dsc resource list 'Adapted/Three' 2>$TestDrive/error.log | ConvertFrom-Json -Depth 10
$LASTEXITCODE | Should -Be 0 -Because (Get-Content $TestDrive/error.log | Out-String)
$out.count | Should -Be 1
$out.type | Should -BeExactly 'Adapted/Three'
$out.kind | Should -BeExactly 'resource'
$out.capabilities | Should -Be @('get', 'set', 'test', 'export')
$parent = (Split-Path -Path (Get-Command dsc).Source -Parent)
$expectedPath = Join-Path -Path $parent -ChildPath 'adaptedTest.dsc.adaptedResource.json'
$out.path | Should -BeExactly $expectedPath
$out.directory | Should -BeExactly $parent
$out.requireAdapter | Should -BeExactly 'Test/Adapter'
$out.schema.embedded | Should -Not -BeNullOrEmpty
}

It 'Adapted resource found in manifest list' {
$out = dsc resource list 'Adapted/Two' 2>$TestDrive/error.log | ConvertFrom-Json -Depth 10
$LASTEXITCODE | Should -Be 0 -Because (Get-Content $TestDrive/error.log | Out-String)
$out.count | Should -Be 1
$out.type | Should -BeExactly 'Adapted/Two'
$out.kind | Should -BeExactly 'resource'
$out.capabilities | Should -Be @('get', 'set', 'test', 'export')
$parent = (Split-Path -Path (Get-Command dsc).Source -Parent)
$expectedPath = Join-Path -Path $parent -ChildPath 'adaptedTest.dsc.adaptedResource.json'
$out.path | Should -BeExactly $expectedPath
$out.directory | Should -BeExactly $parent
$out.requireAdapter | Should -BeExactly 'Test/Adapter'
$out.schema.embedded | Should -Not -BeNullOrEmpty
}

It 'Adapted resource with condition false should not be returned' {
$out = dsc -l debug resource list 'Adapted/Four' 2>$TestDrive/error.log
$errorLog = Get-Content $TestDrive/error.log -Raw
$LASTEXITCODE | Should -Be 0 -Because $errorLog
$out | Should -BeNullOrEmpty -Because $errorLog
$errorLog | Should -Match "Condition '.*?' not met, skipping manifest at .*? for resource 'Adapted/Four" -Because $errorLog
}

It 'Invoking <operation> on adapted resource works' -TestCases @(
@{ operation = 'get' },
@{ operation = 'set' },
@{ operation = 'test' },
@{ operation = 'export' }
){
param($operation)
$out = dsc resource $operation -r Adapted/Three -i '{"one":"3"}' 2>$TestDrive/error.log | ConvertFrom-Json -Depth 10
$LASTEXITCODE | Should -Be 0 -Because (Get-Content $TestDrive/error.log | Out-String)
$parent = (Split-Path -Path (Get-Command dsc).Source -Parent)
$expectedPath = Join-Path -Path $parent -ChildPath 'adaptedTest.dsc.adaptedResource.json'
switch ($operation) {
'get' {
$out.actualState.one | Should -BeExactly 'value3'
$out.actualState.path | Should -BeExactly $expectedPath
}
'set' {
$out.afterState.one | Should -BeExactly 'value3'
$out.afterState.path | Should -BeExactly $expectedPath
}
'test' {
$out.actualState.one | Should -BeExactly 'value3'
$out.actualState.path | Should -BeExactly $expectedPath
$out.inDesiredState | Should -BeFalse
$out.differingProperties | Should -Be @('one')
}
'export' {
$out.resources.count | Should -Be 2
$out.resources[0].type | Should -BeExactly 'Adapted/Three'
$out.resources[0].name | Should -BeExactly 'first'
$out.resources[0].properties.one | Should -BeExactly 'first3'
$out.resources[1].type | Should -BeExactly 'Adapted/Three'
$out.resources[1].name | Should -BeExactly 'second'
$out.resources[1].properties.one | Should -BeExactly 'second3'
}
}
}
}
}
3 changes: 0 additions & 3 deletions dsc/tests/dsc_args.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -210,9 +210,6 @@ resources:
$a_obj = $a[$_] | ConvertFrom-Json
$b_obj = $b[$_] | ConvertFrom-Json
$a_obj.type | Should -Be $b_obj.type
# adapter-based resources should Not be in the results
$a_obj.requireAdapter | Should -BeNullOrEmpty
$b_obj.requireAdapter | Should -BeNullOrEmpty
}
}

Expand Down
6 changes: 3 additions & 3 deletions dsc/tests/dsc_mcp.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ Describe 'Tests for MCP server' {
}

It 'Calling show_dsc_resource works' {
$resource = (dsc resource list | Select-Object -First 1 | ConvertFrom-Json -Depth 20)
$resource = (dsc resource list 'Microsoft/OSInfo' | ConvertFrom-Json -Depth 20)

$mcpRequest = @{
jsonrpc = "2.0"
Expand Down Expand Up @@ -227,10 +227,10 @@ Describe 'Tests for MCP server' {
$response.id | Should -Be 8
$functions = dsc function list --output-format json | ConvertFrom-Json
$response.result.structuredContent.functions.Count | Should -Be $functions.Count

$mcpFunctions = $response.result.structuredContent.functions | Sort-Object name
$dscFunctions = $functions | Sort-Object name

for ($i = 0; $i -lt $dscFunctions.Count; $i++) {
($mcpFunctions[$i].psobject.properties | Measure-Object).Count | Should -BeGreaterOrEqual 8
$mcpFunctions[$i].name | Should -BeExactly $dscFunctions[$i].name -Because ($response.result.structuredContent | ConvertTo-Json -Depth 10 | Out-String)
Expand Down
16 changes: 9 additions & 7 deletions dsc/tests/dsc_resource_input.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Describe 'tests for resource input' {
"-NonInteractive",
"-NoProfile",
"-Command",
"\"{ `\"Hello`\": `\"$env:Hello`\", `\"World`\": `\"$env:World`\", `\"Boolean`\": `\"$env:Boolean`\", `\"StringArray`\": `\"$env:StringArray`\", `\"NumberArray`\": `\"$env:NumberArray`\" }\""
"\"{ `\"Hello`\": `\"$env:Hello`\", `\"World`\": $env:World, `\"Boolean`\": $env:Boolean, `\"StringArray`\": `\"$env:StringArray`\", `\"NumberArray`\": `\"$env:NumberArray`\" }\""
]
},
"set": {
Expand All @@ -27,7 +27,7 @@ Describe 'tests for resource input' {
"-NonInteractive",
"-NoProfile",
"-Command",
"\"{ `\"Hello`\": `\"$env:Hello`\", `\"World`\": `\"$env:World`\", `\"Boolean`\": `\"$env:Boolean`\", `\"StringArray`\": `\"$env:StringArray`\", `\"NumberArray`\": `\"$env:NumberArray`\" }\""
"\"{ `\"Hello`\": `\"$env:Hello`\", `\"World`\": $env:World, `\"Boolean`\": $env:Boolean, `\"StringArray`\": `\"$env:StringArray`\", `\"NumberArray`\": `\"$env:NumberArray`\" }\""
],
"return": "state",
"implementsPretest": true
Expand All @@ -40,7 +40,7 @@ Describe 'tests for resource input' {
"-NonInteractive",
"-NoProfile",
"-Command",
"\"{ `\"Hello`\": `\"$env:Hello`\", `\"World`\": `\"$env:World`\", `\"Boolean`\": `\"$env:Boolean`\", `\"StringArray`\": `\"$env:StringArray`\", `\"NumberArray`\": `\"$env:NumberArray`\" }\""
"\"{ `\"Hello`\": `\"$env:Hello`\", `\"World`\": $env:World, `\"Boolean`\": $env:Boolean, `\"StringArray`\": `\"$env:StringArray`\", `\"NumberArray`\": `\"$env:NumberArray`\" }\""
]
},
"schema": {
Expand All @@ -66,14 +66,14 @@ Describe 'tests for resource input' {
"description": "test"
},
"StringArray": {
"type": "array",
"type": ["array", "string"],
"description": "test",
"items": {
"type": "string"
}
},
"NumberArray": {
"type": "array",
"type": ["array", "string"],
"description": "test",
"items": {
"type": "number"
Expand Down Expand Up @@ -110,8 +110,10 @@ Describe 'tests for resource input' {
}
"@

$result = $json | dsc resource $operation -r Test/EnvVarInput -f - | ConvertFrom-Json
$result.$member.Hello | Should -BeExactly 'foo'
$out = dsc -l trace resource $operation -r Test/EnvVarInput -i $json 2>$TestDrive/error.log
$LASTEXITCODE | Should -Be 0 -Because (Get-Content $TestDrive/error.log -Raw)
$result = $out | ConvertFrom-Json
$result.$member.Hello | Should -BeExactly 'foo' -Because $out
$result.$member.World | Should -Be 2
$result.$member.Boolean | Should -Be 'true'
$result.$member.StringArray | Should -BeExactly 'foo,bar'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"description": "First discovered resource",
"type": "object",
"required": [],
"additionalProperties": false
"additionalProperties": true
}
},
"exitCodes": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"description": "Second discovered resource",
"type": "object",
"required": [],
"additionalProperties": false
"additionalProperties": true
}
},
"exitCodes": {
Expand Down
10 changes: 9 additions & 1 deletion lib/dsc-lib/locales/en-us.toml
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,12 @@ foundManifest = "Found manifest: %{path}"
extensionFound = "Extension '%{extension}' version %{version} found"
adapterFound = "Resource adapter '%{adapter}' version %{version} found"
resourceFound = "Resource '%{resource}' version %{version} found"
adaptedResourceFound = "Adapted resource '%{resource}' version %{version} found"
executableNotFound = "Executable '%{executable}' not found for operation '%{operation}' for resource '%{resource}'"
extensionInvalidVersion = "Extension '%{extension}' version '%{version}' is invalid"
invalidResourceManifest = "Invalid manifest for resource '%{resource}': %{err}"
invalidExtensionManifest = "Invalid manifest for extension '%{extension}': %{err}"
invalidAdaptedResourceManifest = "Invalid manifest for adapted resource '%{resource}': %{err}"
invalidManifestList = "Invalid manifest list '%{resource}': %{err}"
invalidManifestFile = "Invalid manifest file '%{resource}': %{err}"
extensionResourceFound = "Extension found resource '%{resource}'"
Expand All @@ -125,7 +127,10 @@ foundNonAdapterResources = "Found %{count} non-adapter resources"
resourceMissingRequireAdapter = "Resource '%{resource}' is missing 'require_adapter' field."
extensionDiscoverFailed = "Extension '%{extension}' failed to discover resources: %{error}"
conditionNotBoolean = "Condition '%{condition}' did not evaluate to a boolean"
conditionNotMet = "Condition '%{condition}' not met, skipping manifest at '%{path}'"
conditionNotMet = "Condition '%{condition}' not met, skipping manifest at '%{path}' for resource '%{resource}'"
adaptedMissingRequireAdapter = "Adapted resource manifest '%{resource}' missing required 'requireAdapter' property"
adaptedResourcePathNotFound = "Adapted resource '%{resource}' path not found: %{path}"
invalidManifestFileName = "Invalid manifest file name '%{path}'"

[dscresources.commandResource]
invokeGet = "Invoking get for '%{resource}'"
Expand Down Expand Up @@ -214,6 +219,7 @@ validationFailed = "Failed validation"
adapterResourceNotFound = "Adapter resource '%{adapter}' not found"
adapterManifestNotFound = "Adapter manifest for '%{adapter}' not found"
adapterDoesNotSupportDelete = "Adapter '%{adapter}' does not support delete operation"
validatingAgainstSchema = "Validating against resource schema"

[dscresources.resource_manifest]
resourceManifestSchemaTitle = "Resource manifest schema URI"
Expand Down Expand Up @@ -752,6 +758,8 @@ extension = "Extension"
unsupportedCapability = "does not support capability"
setting = "Setting"
invalidRequiredVersion = "Invalid required version '%{version}' for resource '%{resource}'"
resourceMissingDirectory = "Resource is missing 'directory' field."
resourceMissingPath = "Resource is missing 'path' field."

[progress]
failedToSerialize = "Failed to serialize progress JSON: %{json}"
Expand Down
Loading