Skip to content
Draft
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
59 changes: 59 additions & 0 deletions dsc/tests/dsc_extension_discover.tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -212,4 +212,63 @@ Describe 'Discover extension tests' {
$env:TestDrive = $null
}
}

It 'ExtensionsArg is received correctly' {
$extension_json = @'
{
"$schema": "https://aka.ms/dsc/schemas/v3/bundled/resource/manifest.json",
"type": "Test/DiscoverExtensions",
"version": "0.1.0",
"description": "Test discover resource",
"discover": {
"executable": "pwsh",
"args": [
"-NoLogo",
"-NonInteractive",
"-NoProfile",
"-Command",
"./discover.ps1",
{
"extensionsArg": "-Extensions",
"includeQuotes": true
}
]
}
}
'@

$extensions = @{
'.dsc.manifests.yml' = $false
'.dsc.manifests.yaml' = $false
'.dsc.manifests.json' = $false
'.dsc.extension.yml' = $false
'.dsc.extension.yaml' = $false
'.dsc.extension.json' = $false
'.dsc.adaptedresource.yml' = $false
'.dsc.adaptedresource.yaml' = $false
'.dsc.adaptedresource.json' = $false
'.dsc.resource.yml' = $false
'.dsc.resource.yaml' = $false
'.dsc.resource.json' = $false
}

Set-Content -Path "$TestDrive/test.dsc.extension.json" -Value $extension_json
Copy-Item -Path "$toolPath/discover.ps1" -Destination $TestDrive | Out-Null
$env:DSC_RESOURCE_PATH = "$TestDrive" + [System.IO.Path]::PathSeparator + (Split-Path (Get-Command pwsh).Source -Parent)
try {
$out = dsc resource list 2> $TestDrive/error.log | ConvertFrom-Json
$LASTEXITCODE | Should -Be 0
foreach ($resource in $out) {
if ($resource.type.startsWith('TestDiscover/')) {
$extensions[$resource.adaptedContent.extension] = $true
}
}
} finally {
$env:DSC_RESOURCE_PATH = $null
}

foreach ($key in $extensions.Keys) {
$extensions[$key] | Should -BeTrue -Because "Extension $key was not found"
}
}
}
16 changes: 15 additions & 1 deletion extensions/appx/appx-discover.ps1
Original file line number Diff line number Diff line change
@@ -1,6 +1,20 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

[CmdletBinding()]
param(
[Parameter()]
[string]$extensions
)

$fileextensions = [System.Collections.Generic.List[string]]::new()
foreach ($extension in $extensions.Split(',')) {
$fileextensions.Add('*' + $extension)
}

$packages = Get-AppxPackage
foreach ($package in $packages) {
$manifests = Get-ChildItem -Path "$($package.InstallLocation)\*" -File -Include '*.dsc.resource.json','*.dsc.resource.yaml','*.dsc.resource.yml' -ErrorAction Ignore
$manifests = Get-ChildItem -Path "$($package.InstallLocation)\*" -File -Include $fileextensions -ErrorAction Ignore
foreach ($manifest in $manifests) {
@{ manifestPath = $manifest.FullName } | ConvertTo-Json -Compress
}
Expand Down
8 changes: 6 additions & 2 deletions extensions/appx/appx.dsc.extension.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@
"Bypass",
"-NoProfile",
"-Command",
"./appx-discover.ps1"
]
"./appx-discover.ps1",
{
"extensionsArg": "-extensions",
"includeQuotes": true
}
]
}
}
51 changes: 25 additions & 26 deletions extensions/powershell/powershell.discover.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,10 @@
# Licensed under the MIT License.

[CmdletBinding()]
param ()
param (
[Parameter()]
[string]$extensions
)

function Get-CacheFilePath {
if ($IsWindows) {
Expand All @@ -14,33 +17,33 @@ function Get-CacheFilePath {

function Test-CacheValid {
param([string]$CacheFilePath, [string[]]$PSPaths)

if (-not (Test-Path $CacheFilePath)) {
return $false
}

try {
$cache = Get-Content -Raw $CacheFilePath | ConvertFrom-Json

foreach ($entry in $cache.PathInfo.PSObject.Properties) {
$path = $entry.Name
if (-not (Test-Path $path)) {
return $false
}

$currentLastWrite = (Get-Item $path).LastWriteTimeUtc
$cachedLastWrite = [DateTime]$entry.Value

if ($currentLastWrite -ne $cachedLastWrite) {
return $false
}
}

$cachedPaths = [string[]]$cache.PSModulePaths
if ($cachedPaths.Count -ne $PSPaths.Count) {
return $false
}

$diff = Compare-Object $cachedPaths $PSPaths
if ($null -ne $diff) {
return $false
Expand All @@ -60,11 +63,14 @@ function Test-CacheValid {

function Invoke-DscResourceDiscovery {
[CmdletBinding()]
param()

param(
[Parameter()]
[string]$extensions
)

begin {
$psPaths = $env:PSModulePath -split [System.IO.Path]::PathSeparator | Where-Object { $_ -notmatch 'WindowsPowerShell' }

$cacheFilePath = Get-CacheFilePath
$useCache = Test-CacheValid -CacheFilePath $cacheFilePath -PSPaths $psPaths
}
Expand All @@ -74,17 +80,10 @@ function Invoke-DscResourceDiscovery {
$manifests = $cache.Manifests
} else {
$manifests = $psPaths | ForEach-Object -Parallel {
$searchPatterns = @(
'*.dsc.resource.json'
'*.dsc.resource.yaml'
'*.dsc.resource.yml'
'*.dsc.adaptedresource.json'
'*.dsc.adaptedresource.yaml'
'*.dsc.adaptedresource.yml'
'*.dsc.manifests.json'
'*.dsc.manifests.yaml'
'*.dsc.manifests.yml'
)
$searchPatterns = [System.Collections.Generic.List[string]]::new()
foreach ($extension in ($using:extensions).Split(',')) {
$searchPatterns.Add('*' + $extension)
}
$enumOptions = [System.IO.EnumerationOptions]@{ IgnoreInaccessible = $true; RecurseSubdirectories = $true }
foreach ($pattern in $searchPatterns) {
try {
Expand All @@ -94,7 +93,7 @@ function Invoke-DscResourceDiscovery {
} catch { }
}
} -ThrottleLimit 10

$pathInfo = @{}
foreach ($path in $psPaths) {
$item = Get-Item -LiteralPath $path -ErrorAction Ignore
Expand All @@ -107,13 +106,13 @@ function Invoke-DscResourceDiscovery {
}
}
}

$cacheObject = @{
PSModulePaths = $psPaths
PathInfo = $pathInfo
Manifests = $manifests
}

$cacheDir = Split-Path $cacheFilePath -Parent
if (-not (Test-Path $cacheDir)) {
New-Item -ItemType Directory -Path $cacheDir -Force | Out-Null
Expand All @@ -129,4 +128,4 @@ function Invoke-DscResourceDiscovery {
}
}

Invoke-DscResourceDiscovery
Invoke-DscResourceDiscovery -extensions $extensions
6 changes: 5 additions & 1 deletion extensions/powershell/powershell.dsc.extension.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@
"Bypass",
"-NoProfile",
"-Command",
"./powershell.discover.ps1"
"./powershell.discover.ps1",
{
"extensionsArg": "-extensions",
"includeQuotes": true
}
]
}
}
51 changes: 43 additions & 8 deletions extensions/test/discover/discover.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,51 @@
[CmdletBinding()]
param(
[Parameter()]
[switch]$RelativePath
[switch]$RelativePath,
[Parameter()]
[string]$Extensions
)

Get-ChildItem -Path $PSScriptRoot/resources/*.json | ForEach-Object {
$resource = [pscustomobject]@{
manifestPath = if ($RelativePath) {
Resolve-Path -Path $_.FullName -Relative
} else {
$_.FullName
if ($Extensions) {
$count = 1
foreach ($extension in $Extensions.Split(',')) {
$resource = [pscustomobject]@{
manifestContent = @{
'$schema' = "https://aka.ms/dsc/schemas/v3/bundled/adaptedresource/manifest.json"
type = "TestDiscover/$count"
kind = "resource"
version = "1.0.0"
capabilities = @("get")
description = "Test discover $count"
requireAdapter = "Test/Adapter"
content = @{
extension = $extension
}
schema = @{
embedded = @{
'$schema' = "http://json-schema.org/draft-07/schema#"
type = "object"
properties = @{
extension = @{
type = "string"
}
}
}
}
}
}
$resource | ConvertTo-Json -Compress -Depth 10
$count++
}
} else {
Get-ChildItem -Path $PSScriptRoot/resources/*.json | ForEach-Object {
$resource = [pscustomobject]@{
manifestPath = if ($RelativePath) {
Resolve-Path -Path $_.FullName -Relative
} else {
$_.FullName
}
}
$resource | ConvertTo-Json -Compress
}
$resource | ConvertTo-Json -Compress
}
2 changes: 1 addition & 1 deletion lib/dsc-lib/locales/en-us.toml
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ resourceFound = "Resource '%{resource}' version %{version} found"
adaptedResourceFound = "Adapted resource '%{resource}' version %{version} found"
executableNotFound = "Executable '%{executable}' not found for operation '%{operation}' for resource '%{resource}'"
invalidResourceManifest = "Invalid manifest for resource '%{resource}': %{err}"
invalidExtensionManifest = "Invalid manifest for extension '%{extension}': %{err}"
invalidExtensionManifest = "Invalid manifest for extension '%{resource}': %{err}"
invalidAdaptedResourceManifest = "Invalid manifest for adapted resource '%{resource}': %{err}"
invalidManifestList = "Invalid manifest list '%{resource}': %{err}"
invalidManifestFile = "Invalid manifest file '%{resource}': %{err}"
Expand Down
11 changes: 6 additions & 5 deletions lib/dsc-lib/src/discovery/command_discovery.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,11 @@ use tracing::{debug, info, trace, warn};
use crate::util::get_setting;
use crate::util::{canonicalize_which, get_exe_path};

const DSC_ADAPTED_RESOURCE_EXTENSIONS: [&str; 3] = [".dsc.adaptedresource.json", ".dsc.adaptedresource.yaml", ".dsc.adaptedresource.yml"];
const DSC_EXTENSION_EXTENSIONS: [&str; 3] = [".dsc.extension.json", ".dsc.extension.yaml", ".dsc.extension.yml"];
const DSC_MANIFEST_LIST_EXTENSIONS: [&str; 3] = [".dsc.manifests.json", ".dsc.manifests.yaml", ".dsc.manifests.yml"];
const DSC_RESOURCE_EXTENSIONS: [&str; 3] = [".dsc.resource.json", ".dsc.resource.yaml", ".dsc.resource.yml"];
// NOTE: if new types of file extensions are added, ensure they are added to `process_discover_args` in `lib/dsc-lib/src/extensions/discover.rs`
pub const DSC_ADAPTED_RESOURCE_EXTENSIONS: [&str; 3] = [".dsc.adaptedresource.json", ".dsc.adaptedresource.yaml", ".dsc.adaptedresource.yml"];
pub const DSC_EXTENSION_EXTENSIONS: [&str; 3] = [".dsc.extension.json", ".dsc.extension.yaml", ".dsc.extension.yml"];
pub const DSC_MANIFEST_LIST_EXTENSIONS: [&str; 3] = [".dsc.manifests.json", ".dsc.manifests.yaml", ".dsc.manifests.yml"];
pub const DSC_RESOURCE_EXTENSIONS: [&str; 3] = [".dsc.resource.json", ".dsc.resource.yaml", ".dsc.resource.yml"];

static ADAPTERS: LazyLock<RwLock<DiscoveryResourceCache>> = LazyLock::new(|| RwLock::new(DiscoveryResourceCache::new()));
static RESOURCES: LazyLock<RwLock<DiscoveryResourceCache>> = LazyLock::new(|| RwLock::new(DiscoveryResourceCache::new()));
Expand Down Expand Up @@ -775,7 +776,7 @@ pub fn load_manifest(path: &Path) -> Result<Vec<ImportedManifest>, DscError> {
Err(DscError::InvalidManifest(t!("discovery.commandDiscovery.invalidManifestFile", resource = path.to_string_lossy()).to_string()))
}

fn load_adapted_resource_manifest(path: &Path, manifest: &AdaptedDscResourceManifest) -> Result<DscResource, DscError> {
pub fn load_adapted_resource_manifest(path: &Path, manifest: &AdaptedDscResourceManifest) -> Result<DscResource, DscError> {
if manifest.version.is_date_version() {
warn!("{}", t!(
"discovery.commandDiscovery.invalidManifestVersion",
Expand Down
Loading
Loading