From 63c0fb54a174760153a325370c394a6bba4b1f32 Mon Sep 17 00:00:00 2001 From: alx9r Date: Wed, 18 Jan 2017 09:05:06 -0800 Subject: [PATCH] tolerate multiple versions of a DSC resource (#34) fix problem where Get-Module Name was returning multiple versions change ConvertTo-ConfigDocument to bind to the latest encountered version of a DSC resource resolve #33 --- Functions/classResourceInvokerType.Tests.ps1 | 8 ++--- Functions/classResourceInvokerType.ps1 | 10 +++++- Functions/configDocumentType.Tests.ps1 | 38 ++++++++++++-------- Functions/configDocumentType.ps1 | 12 ++++--- 4 files changed, 44 insertions(+), 24 deletions(-) diff --git a/Functions/classResourceInvokerType.Tests.ps1 b/Functions/classResourceInvokerType.Tests.ps1 index c571ac9..1996107 100644 --- a/Functions/classResourceInvokerType.Tests.ps1 +++ b/Functions/classResourceInvokerType.Tests.ps1 @@ -6,11 +6,11 @@ $records = @{} $stubResourceNames = @( 'StubResource1AFriendlyName','StubResource1BFriendlyName', 'StubResource2A','StubResource2B','StubResource2C', - 'StubResource3A','StubResource3B' + 'StubResource3A','StubResource3B','StubResource4A' ) $stubClassResourceNames = @( 'StubResource2A','StubResource2B','StubResource2C', - 'StubResource3A','StubResource3B' + 'StubResource3A','StubResource3B','StubResource4A' ) $stubMofResourceNames = @( 'StubResource1AFriendlyName','StubResource1BFriendlyName' @@ -39,7 +39,7 @@ Describe New-ClassResourceObject { { $record = $records.$resourceName It "returns correct resource object for $resourceName" { - $r = $record.DscResource | New-ClassResourceObject + $r = $record.DscResource | Select -First 1 | New-ClassResourceObject $r.Count | Should be 1 $r.GetType().Name | Should be $resourceName } @@ -51,7 +51,7 @@ Describe Test-ClassResourceType { { $record = $records.$resourceName It "$resourceName is a Class resource type" { - $r = $record.DscResource | Test-ClassResourceType + $r = $record.DscResource | Select -First 1 | Test-ClassResourceType $r.Count | Should be 1 $r | Should beOfType bool $r | Should be $true diff --git a/Functions/classResourceInvokerType.ps1 b/Functions/classResourceInvokerType.ps1 index 86c2a10..dd29929 100644 --- a/Functions/classResourceInvokerType.ps1 +++ b/Functions/classResourceInvokerType.ps1 @@ -67,9 +67,17 @@ function New-ClassResourceObject ) { $module | Import-Module + + # $module isn't always a fully-populated object at this point + # We get the loaded modules(s) of the right name and select the + # one at the right path. + $liveModule = Get-Module $module.Name | + ? { $_.ModuleBase -eq $module.ModuleBase } | + Select -First 1 + try { - $object = (Get-Module $module.Name).NewBoundScriptBlock( + $object = $liveModule.NewBoundScriptBlock( [scriptblock]::Create("[$($DscResource.Name)]::new()") ).InvokeReturnAsIs() } diff --git a/Functions/configDocumentType.Tests.ps1 b/Functions/configDocumentType.Tests.ps1 index 501df51..57185ba 100644 --- a/Functions/configDocumentType.Tests.ps1 +++ b/Functions/configDocumentType.Tests.ps1 @@ -232,27 +232,37 @@ Describe ConvertTo-ConfigDocument { } Context 'duplicate Resources' { $h = @{} - It 'throws correct exception type' { - $h.CallSite = & {$MyInvocation} + It 'tolerates exact duplicates' { $raw = New-RawConfigDocument 'DocumentName' { Get-DscResource StubResource2A | Import-DscResource Get-DscResource StubResource2A | Import-DscResource StubResource2A ConfigName2A @{} } - try - { - $raw | ConvertTo-ConfigDocument - } - catch [FormatException] - { - $h.Exception = $_ + $r = $raw | ConvertTo-ConfigDocument + $r.Resources.Count | Should be 1 + $r.Resources.Keys[0] | Should be '[StubResource2A]ConfigName2A' + } + It 'binds to the later version...' { + $raw = New-RawConfigDocument 'DocumentName' { + Get-DscResource StubResource4A | ? {$_.Version -eq '1.1'} | Import-DscResource + Get-DscResource StubResource4A | ? {$_.Version -eq '1.0'} | Import-DscResource + StubResource4A ConfigName4A @{} } - $h.Exception | Should not beNullOrEmpty + $r = $raw | ConvertTo-ConfigDocument + $r.Resources.Count | Should be 1 + $r.Resources.'[StubResource4A]ConfigName4A'.Resource.Version | + Should be '1.1' } - It 'the exception shows the filename of the offending call' {} - It 'the exception shows the line number of the offending call' {} - It 'the exception contains an informative message' { - $h.Exception.ToString() | Should match 'Duplicate resource named StubResource2A' + It '...regardless of order encountered' { + $raw = New-RawConfigDocument 'DocumentName' { + Get-DscResource StubResource4A | ? {$_.Version -eq '1.0'} | Import-DscResource + Get-DscResource StubResource4A | ? {$_.Version -eq '1.1'} | Import-DscResource + StubResource4A ConfigName4A @{} + } + $r = $raw | ConvertTo-ConfigDocument + $r.Resources.Count | Should be 1 + $r.Resources.'[StubResource4A]ConfigName4A'.Resource.Version | + Should be '1.1' } } Context 'bad resource binding' { diff --git a/Functions/configDocumentType.ps1 b/Functions/configDocumentType.ps1 index ea63c5c..3c05e6d 100644 --- a/Functions/configDocumentType.ps1 +++ b/Functions/configDocumentType.ps1 @@ -88,12 +88,14 @@ function ConvertTo-ConfigDocument $resources = [System.Collections.Generic.Dictionary[System.String,Microsoft.PowerShell.DesiredStateConfiguration.DscResourceInfo]]::new() foreach ( $resource in $InputObject.DscResources ) { - # check for duplicate resource names - if ( $resources.ContainsKey( $resource.Name ) ) + # skip duplicate resources with lower version numbers + if + ( + $resources.ContainsKey( $resource.Name ) -and + ( $resource.Version -lt $resources.($resource.Name).Version ) + ) { - throw [System.FormatException]::new( - "Duplicate resource named $($resource.Name)" - ) + continue } $resources.($resource.Name) = $resource