From 8f905ab09971a3df194ca4e5a970ed7d6374db6c Mon Sep 17 00:00:00 2001 From: Katie Keim Date: Tue, 5 Jul 2016 16:48:30 -0700 Subject: [PATCH 1/9] Converting Pester install to PSGallery in appveyor.yml. --- appveyor.yml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index d992b90..1a512c8 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -3,12 +3,11 @@ #---------------------------------# version: 1.4.{build}.0 install: - - cinst -y pester - - git clone https://github.com/PowerShell/DscResource.Tests - - ps: Push-Location - - cd DscResource.Tests - - ps: Import-Module .\TestHelper.psm1 -force - - ps: Pop-Location + - git clone https://github.com/PowerShell/DscResource.Tests + - ps: | + Import-Module -Name .\DscResource.Tests\TestHelper.psm1 -Force + Install-PackageProvider -Name NuGet -MinimumVersion 2.8.5.201 -Force + Install-Module -Name Pester -Repository PSGallery -Force #---------------------------------# # build configuration # From ba76a32fa990993245fb3d037d920b37591e3f21 Mon Sep 17 00:00:00 2001 From: Katie Keim Date: Tue, 5 Jul 2016 16:48:52 -0700 Subject: [PATCH 2/9] Updating README. --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 298fd77..881260e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![Build status](https://ci.appveyor.com/api/projects/status/6a59vfritv4kbc7d/branch/master?svg=true)](https://ci.appveyor.com/project/PowerShell/xfailovercluster/branch/master) +[![Build status](https://ci.appveyor.com/api/projects/status/6a59vfritv4kbc7d/branch/master?svg=true)](https://ci.appveyor.com/project/PowerShell/xfailovercluster/branch/master) # xFailOverCluster @@ -54,6 +54,7 @@ For more information about cluster preferred owners please see: http://support.m ## Versions ### Unreleased +* Converted appveyor.yml to install Pester from PSGallery instead of from Chocolatey. ### 1.4.0.0 * xClusterDisk: Fixed Test-TargetResource logic From 9edd81f3926a77b19a26fe80b86ccf5ac3eb6d47 Mon Sep 17 00:00:00 2001 From: Troy Ault Date: Thu, 4 Aug 2016 14:49:28 -0400 Subject: [PATCH 3/9] Pulling in items from PR11 to fix merge conflicts --- DSCResources/MSFT_xCluster/MSFT_xCluster.psm1 | 13 +- .../MSFT_xClusterQuorum.psm1 | 145 +++++ .../MSFT_xClusterQuorum.schema.mof | 9 + README.md | 35 +- Tests/MSFT_xClusterQuorum.Tests.ps1 | 595 ++++++++++++++++++ 5 files changed, 762 insertions(+), 35 deletions(-) create mode 100644 DSCResources/MSFT_xClusterQuorum/MSFT_xClusterQuorum.psm1 create mode 100644 DSCResources/MSFT_xClusterQuorum/MSFT_xClusterQuorum.schema.mof create mode 100644 Tests/MSFT_xClusterQuorum.Tests.ps1 diff --git a/DSCResources/MSFT_xCluster/MSFT_xCluster.psm1 b/DSCResources/MSFT_xCluster/MSFT_xCluster.psm1 index e31969b..d922780 100644 --- a/DSCResources/MSFT_xCluster/MSFT_xCluster.psm1 +++ b/DSCResources/MSFT_xCluster/MSFT_xCluster.psm1 @@ -8,7 +8,6 @@ # function Get-TargetResource { - [OutputType([Hashtable])] param ( [parameter(Mandatory)] @@ -36,7 +35,7 @@ function Get-TargetResource throw "Can't find the cluster $Name" } - $address = Get-ClusterGroup -Cluster $Name | Get-ClusterResource -Name 'Cluster IP Address' | Get-ClusterParameter 'Address' + $address = Get-ClusterGroup -Cluster $Name -Name "Cluster IP Address" | Get-ClusterParameter "Address" } finally { @@ -50,9 +49,8 @@ function Get-TargetResource $retvalue = @{ Name = $Name - StaticIPAddress = $address.Value + IPAddress = $address.Value } - $retvalue } # @@ -106,6 +104,8 @@ function Set-TargetResource New-Cluster -Name $Name -Node $env:COMPUTERNAME -StaticAddress $StaticIPAddress -NoStorage -Force + Start-Sleep -Seconds 60 + Write-Verbose -Message "Created Cluster $Name" } else @@ -119,7 +119,7 @@ function Set-TargetResource { if ($node.Name -eq $env:COMPUTERNAME) { - if ($node.State -eq 'Down') + if ($node.State -eq "Down") { Write-Verbose -Message "node $env:COMPUTERNAME was down, need remove it from the list." @@ -158,7 +158,6 @@ function Set-TargetResource # function Test-TargetResource { - [OutputType([Boolean])] param ( [parameter(Mandatory)] @@ -203,7 +202,7 @@ function Test-TargetResource { if ($node.Name -eq $env:COMPUTERNAME) { - if ($node.State -eq 'Up') + if ($node.State -eq "Up") { $bRet = $true } diff --git a/DSCResources/MSFT_xClusterQuorum/MSFT_xClusterQuorum.psm1 b/DSCResources/MSFT_xClusterQuorum/MSFT_xClusterQuorum.psm1 new file mode 100644 index 0000000..783b25c --- /dev/null +++ b/DSCResources/MSFT_xClusterQuorum/MSFT_xClusterQuorum.psm1 @@ -0,0 +1,145 @@ + +function Get-TargetResource +{ + [CmdletBinding()] + [OutputType([Hashtable])] + param + ( + [Parameter(Mandatory = $true)] + [ValidateSet('Yes')] + [String] $IsSingleInstance, + + [Parameter(Mandatory = $false)] + [ValidateSet('NodeMajority', 'NodeAndDiskMajority', 'NodeAndFileShareMajority', 'DiskOnly')] + [String] $Type, + + [Parameter(Mandatory = $false)] + [String] $Resource + ) + + $ClusterQuorum = Get-ClusterQuorum + + switch ($ClusterQuorum.QuorumType) + { + # WS2016 only + 'Majority' { + if ($ClusterQuorum.QuorumResource -eq $null) + { + $ClusterQuorumType = 'NodeMajority' + } + elseif ($ClusterQuorum.QuorumResource.ResourceType.DisplayName -eq 'Physical Disk') + { + $ClusterQuorumType = 'NodeAndDiskMajority' + } + elseif ($ClusterQuorum.QuorumResource.ResourceType.DisplayName -eq 'File Share Witness') + { + $ClusterQuorumType = 'NodeAndFileShareMajority' + } + else + { + throw "Unknown quorum resource: $($ClusterQuorum.QuorumResource)" + } + } + + # WS2012R2 only + 'NodeMajority' { + $ClusterQuorumType = 'NodeMajority' + } + 'NodeAndDiskMajority' { + $ClusterQuorumType = 'NodeAndDiskMajority' + } + 'NodeAndFileShareMajority' { + $ClusterQuorumType = 'NodeAndFileShareMajority' + } + + # All + 'DiskOnly' { + $ClusterQuorumType = 'DiskOnly' + } + + # Default + default { + throw "Unknown quorum type: $($ClusterQuorum.QuorumType)" + } + } + + if ($ClusterQuorumType -eq 'NodeAndFileShareMajority') + { + $ClusterQuorumResource = $ClusterQuorum.QuorumResource | Get-ClusterParameter -Name SharePath | Select-Object -ExpandProperty Value + } + else + { + $ClusterQuorumResource = [String] $ClusterQuorum.QuorumResource.Name + } + + @{ + IsSingleInstance = $IsSingleInstance + Type = $ClusterQuorumType + Resource = $ClusterQuorumResource + } +} + +function Set-TargetResource +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [ValidateSet('Yes')] + [String] $IsSingleInstance, + + [Parameter(Mandatory = $false)] + [ValidateSet('NodeMajority', 'NodeAndDiskMajority', 'NodeAndFileShareMajority', 'DiskOnly')] + [String] $Type, + + [Parameter(Mandatory = $false)] + [String] $Resource + ) + + switch ($Type) + { + 'NodeMajority' { + Set-ClusterQuorum -NoWitness + } + + 'NodeAndDiskMajority' { + Set-ClusterQuorum -DiskWitness $Resource + } + + 'NodeAndFileShareMajority' { + Set-ClusterQuorum -FileShareWitness $Resource + } + + 'DiskOnly' { + Set-ClusterQuorum -DiskOnly $Resource + } + } +} + +function Test-TargetResource +{ + [CmdletBinding()] + [OutputType([Boolean])] + param + ( + [Parameter(Mandatory = $true)] + [ValidateSet('Yes')] + [String] $IsSingleInstance, + + [Parameter(Mandatory = $false)] + [ValidateSet('NodeMajority', 'NodeAndDiskMajority', 'NodeAndFileShareMajority', 'DiskOnly')] + [String] $Type, + + [Parameter(Mandatory = $false)] + [String] $Resource + ) + + $CurrentQuorum = Get-TargetResource -IsSingleInstance $IsSingleInstance + + return ( + ($CurrentQuorum.Type -eq $Type) -and + ($CurrentQuorum.Resource -eq $Resource) + ) +} + +Export-ModuleMember -Function *-TargetResource diff --git a/DSCResources/MSFT_xClusterQuorum/MSFT_xClusterQuorum.schema.mof b/DSCResources/MSFT_xClusterQuorum/MSFT_xClusterQuorum.schema.mof new file mode 100644 index 0000000..9225793 --- /dev/null +++ b/DSCResources/MSFT_xClusterQuorum/MSFT_xClusterQuorum.schema.mof @@ -0,0 +1,9 @@ +[ClassVersion("1.0.0.0"), FriendlyName("xClusterQuorum")] +class MSFT_xClusterQuorum : OMI_BaseResource +{ + [Key, ValueMap{"Yes"}, Values{"Yes"}] string IsSingleInstance; + + [Write, ValueMap{"NodeMajority", "NodeAndDiskMajority", "NodeAndFileShareMajority", "DiskOnly"}, Values{"NodeMajority", "NodeAndDiskMajority", "NodeAndFileShareMajority", "DiskOnly"}] string Type; + + [Write] String Resource; +}; \ No newline at end of file diff --git a/README.md b/README.md index 881260e..0a36085 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,9 @@ -[![Build status](https://ci.appveyor.com/api/projects/status/6a59vfritv4kbc7d/branch/master?svg=true)](https://ci.appveyor.com/project/PowerShell/xfailovercluster/branch/master) +[![Build status](https://ci.appveyor.com/api/projects/status/6a59vfritv4kbc7d/branch/master?svg=true)](https://ci.appveyor.com/project/PowerShell/xfailovercluster/branch/master) # xFailOverCluster The **xFailOverCluster** DSC modules contains **xCluster** and **xWaitForCluster** resources for creating and configuring failover clusters. -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). -For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. - ## Contributing Please check out common DSC Resources [contributing guidelines](https://github.com/PowerShell/DscResource.Kit/blob/master/CONTRIBUTING.md). @@ -22,13 +19,11 @@ Please check out common DSC Resources [contributing guidelines](https://github.c * **StaticIPAddress**: Static IP Address of the cluster * **DomainAdministratorCredential**: Credential used to create the cluster -### xClusterNetwork (Unreleased) +### xClusterQuorum (Unreleased) -* **Address**: The network address (e.g. 192.168.0.0) -* **AddressMask**: The network mask (e.g. 255.255.255.0) -* **Name**: The network label or name -* **Role**: Network role: *0 = None, 1 = Cluster Only, 3 = Clsuter and Client* -* **Metric**: The internal metric for the networks +* **IsSingleInstance** Always set to `Yes` to prevent multiple quorum settings per cluster. +* **Type** Quorum type to use: *NodeMajority*, *NodeAndDiskMajority*, *NodeAndFileShareMajority*, *DiskOnly* +* **Resource** The name of the disk or file share resource to use as witness. Is optional with *NodeMajority* type. ### xClusterDisk (Unreleased) @@ -36,14 +31,6 @@ Please check out common DSC Resources [contributing guidelines](https://github.c * **Ensure**: Define if the cluster disk should be added (Present) or removed (Absent) * **Label**: The disk label inside the Failover Cluster -### xClusterPreferredOwner (Unreleased) -For more information about cluster preferred owners please see: http://support.microsoft.com/kb/299631 -* **ClusterGroup**: Cluster group name -* **ClusterName**: Cluster name -* **Nodes**: Selected cluster nodes. -* **ClusterResources**: Selected cluster resources -* **Ensure**: Whether an owner should be present or removed - ### xWaitForCluster * **Name**: Name of the cluster to wait for @@ -54,21 +41,13 @@ For more information about cluster preferred owners please see: http://support.m ## Versions ### Unreleased -* Converted appveyor.yml to install Pester from PSGallery instead of from Chocolatey. - -### 1.4.0.0 -* xClusterDisk: Fixed Test-TargetResource logic - -### 1.3.0.0 -* Added xClusterNetwork resource +* Added xClusterQuorum resource with options *NodeMajority*, *NodeAndDiskMajority*, *NodeAndFileShareMajority*, *DiskOnly* * Added xClusterDisk resource -* Added xClusterPreferredOwner resource -* Resolved issue: Failing Get-TargetResource in xCluster ### 1.2.0.0 -* xCluster: Added -NoStorage switch to add-clusternode. This prevents disks from being automatically added when joining a node to a cluster +* xCluster: Added -NoStorage switch to add-clusterNode. This prevents disks from being automatically added when joining a node to a cluster ### 1.1.0.0 diff --git a/Tests/MSFT_xClusterQuorum.Tests.ps1 b/Tests/MSFT_xClusterQuorum.Tests.ps1 new file mode 100644 index 0000000..00ed8da --- /dev/null +++ b/Tests/MSFT_xClusterQuorum.Tests.ps1 @@ -0,0 +1,595 @@ +[CmdletBinding()] +param +( +) + +if (!$PSScriptRoot) +{ + $PSScriptRoot = [System.IO.Path]::GetDirectoryName($MyInvocation.MyCommand.Path) +} + +$RootPath = (Resolve-Path -Path "$PSScriptRoot\..").Path +$ModuleName = 'MSFT_xClusterQuorum' + +try +{ + if (-not (Get-WindowsFeature -Name RSAT-Clustering-PowerShell -ErrorAction Stop).Installed) + { + Add-WindowsFeature -Name RSAT-Clustering-PowerShell -ErrorAction Stop + } +} +catch +{ + Write-Warning $_ +} + +Import-Module (Join-Path -Path $RootPath -ChildPath "DSCResources\$ModuleName\$ModuleName.psm1") -Force + + +## General test for the xClusterQuorum resource + +Describe 'xClusterQuorum' { + + InModuleScope $ModuleName { + + $TestParameter = @{ + IsSingleInstance = 'Yes' + Type = 'NodeAndDiskMajority' + Resource = 'Witness' + } + + Mock -CommandName 'Get-ClusterQuorum' -MockWith { + [PSCustomObject] @{ + Cluster = 'CLUSTER01' + QuorumResource = 'Witness' + QuorumType = 'NodeAndDiskMajority' + } + } + + Mock -CommandName 'Set-ClusterQuorum' -MockWith { + } + + Context 'Validate Get-TargetResource method' { + + It 'Returns a [System.Collection.Hashtable] type' { + + $Result = Get-TargetResource @TestParameter + + $Result -is [System.Collections.Hashtable] | Should Be $true + } + } + + Context 'Validate Set-TargetResource method' { + + It 'Returns nothing' { + + $Result = Set-TargetResource @TestParameter + + $Result -eq $null | Should Be $true + } + } + + Context 'Validate Test-TargetResource method' { + + It 'Returns a [System.Boolean] type' { + + $Result = Test-TargetResource @TestParameter + + $Result -is [System.Boolean] | Should Be $true + } + } + } +} + + +## Test NodeMajority quorum type + +Describe 'xClusterQuorum (NodeMajority / WS2012R2)' { + + InModuleScope $ModuleName { + + $TestParameter = @{ + IsSingleInstance = 'Yes' + Type = 'NodeMajority' + Resource = '' + } + + Mock -CommandName 'Get-ClusterQuorum' -MockWith { + [PSCustomObject] @{ + Cluster = 'CLUSTER01' + QuorumType = 'NodeMajority' + QuorumResource = $null + } + } + + Mock -CommandName 'Set-ClusterQuorum' -ParameterFilter { $NoWitness -eq $true } -MockWith { + } + + Context 'Validate Get-TargetResource method' { + + It 'Returns current configuration' { + + $Result = Get-TargetResource @TestParameter + + $Result.IsSingleInstance | Should Be $TestParameter.IsSingleInstance + $Result.Type | Should Be $TestParameter.Type + $Result.Resource | Should Be $TestParameter.Resource + } + } + + Context 'Validate Set-TargetResource method' { + + It 'Set the new configuration' { + + $Result = Set-TargetResource @TestParameter + + Assert-MockCalled -CommandName 'Set-ClusterQuorum' -ParameterFilter { $NoWitness -eq $true } -Times 1 + } + } + + Context 'Validate Test-TargetResource method' { + + It 'Check the current configuration' { + + $Result = Test-TargetResource @TestParameter + + $Result | Should Be $true + } + } + } +} + +Describe 'xClusterQuorum (NodeMajority / WS2016Prev)' { + + InModuleScope $ModuleName { + + $TestParameter = @{ + IsSingleInstance = 'Yes' + Type = 'NodeMajority' + Resource = '' + } + + Mock -CommandName 'Get-ClusterQuorum' -MockWith { + [PSCustomObject] @{ + Cluster = 'CLUSTER01' + QuorumType = 'Majority' + QuorumResource = $null + } + } + + Mock -CommandName 'Set-ClusterQuorum' -ParameterFilter { $NoWitness -eq $true } -MockWith { + } + + Context 'Validate Get-TargetResource method' { + + It 'Returns current configuration' { + + $Result = Get-TargetResource @TestParameter + + $Result.IsSingleInstance | Should Be $TestParameter.IsSingleInstance + $Result.Type | Should Be $TestParameter.Type + $Result.Resource | Should Be $TestParameter.Resource + } + } + + Context 'Validate Set-TargetResource method' { + + It 'Set the new configuration' { + + $Result = Set-TargetResource @TestParameter + + Assert-MockCalled -CommandName 'Set-ClusterQuorum' -ParameterFilter { $NoWitness -eq $true } -Times 1 + } + } + + Context 'Validate Test-TargetResource method' { + + It 'Check the current configuration' { + + $Result = Test-TargetResource @TestParameter + + $Result | Should Be $true + } + } + } +} + + +## Test NodeAndDiskMajority quorum type + +Describe 'xClusterQuorum (NodeAndDiskMajority / WS2012R2)' { + + InModuleScope $ModuleName { + + $TestParameter = @{ + IsSingleInstance = 'Yes' + Type = 'NodeAndDiskMajority' + Resource = 'Witness' + } + + Mock -CommandName 'Get-ClusterQuorum' -MockWith { + [PSCustomObject] @{ + Cluster = 'CLUSTER01' + QuorumType = 'NodeAndDiskMajority' + QuorumResource = [PSCustomObject] @{ + Name = 'Witness' + OwnerGroup = 'Cluster Group' + ResourceType = [PSCustomObject] @{ + DisplayName = 'Physical Disk' + } + } + } + } + + Mock -CommandName 'Set-ClusterQuorum' -ParameterFilter { $DiskWitness -eq 'Witness' } -MockWith { + } + + Context 'Validate Get-TargetResource method' { + + It 'Returns current configuration' { + + $Result = Get-TargetResource @TestParameter + + $Result.IsSingleInstance | Should Be $TestParameter.IsSingleInstance + $Result.Type | Should Be $TestParameter.Type + $Result.Resource | Should Be $TestParameter.Resource + } + } + + Context 'Validate Set-TargetResource method' { + + It 'Set the new configuration' { + + $Result = Set-TargetResource @TestParameter + + Assert-MockCalled -CommandName 'Set-ClusterQuorum' -ParameterFilter { $DiskWitness -eq 'Witness' } -Times 1 + } + } + + Context 'Validate Test-TargetResource method' { + + It 'Check the current configuration' { + + $Result = Test-TargetResource @TestParameter + + $Result | Should Be $true + } + } + } +} + +Describe 'xClusterQuorum (NodeAndDiskMajority / WS2016Prev)' { + + InModuleScope $ModuleName { + + $TestParameter = @{ + IsSingleInstance = 'Yes' + Type = 'NodeAndDiskMajority' + Resource = 'Witness' + } + + Mock -CommandName 'Get-ClusterQuorum' -MockWith { + [PSCustomObject] @{ + Cluster = 'CLUSTER01' + QuorumType = 'Majority' + QuorumResource = [PSCustomObject] @{ + Name = 'Witness' + OwnerGroup = 'Cluster Group' + ResourceType = [PSCustomObject] @{ + DisplayName = 'Physical Disk' + } + } + } + } + + Mock -CommandName 'Set-ClusterQuorum' -ParameterFilter { $DiskWitness -eq 'Witness' } -MockWith { + } + + Context 'Validate Get-TargetResource method' { + + It 'Returns current configuration' { + + $Result = Get-TargetResource @TestParameter + + $Result.IsSingleInstance | Should Be $TestParameter.IsSingleInstance + $Result.Type | Should Be $TestParameter.Type + $Result.Resource | Should Be $TestParameter.Resource + } + } + + Context 'Validate Set-TargetResource method' { + + It 'Set the new configuration' { + + $Result = Set-TargetResource @TestParameter + + Assert-MockCalled -CommandName 'Set-ClusterQuorum' -ParameterFilter { $DiskWitness -eq 'Witness' } -Times 1 + } + } + + Context 'Validate Test-TargetResource method' { + + It 'Check the current configuration' { + + $Result = Test-TargetResource @TestParameter + + $Result | Should Be $true + } + } + } +} + + +## Test NodeAndFileShareMajority quorum type + +Describe 'xClusterQuorum (NodeAndFileShareMajority / WS2012R2)' { + + InModuleScope $ModuleName { + + $TestParameter = @{ + IsSingleInstance = 'Yes' + Type = 'NodeAndFileShareMajority' + Resource = '\\FILE01\CLUSTER01' + } + + Mock -CommandName 'Get-ClusterQuorum' -MockWith { + [PSCustomObject] @{ + Cluster = 'CLUSTER01' + QuorumType = 'NodeAndFileShareMajority' + QuorumResource = [PSCustomObject] @{ + Name = 'File Share Witness' + OwnerGroup = 'Cluster Group' + ResourceType = [PSCustomObject] @{ + DisplayName = 'File Share Witness' + } + } + } + } + + Mock -CommandName 'Get-ClusterParameter' -ParameterFilter { $Name -eq 'SharePath' } -MockWith { + @( + [PSCustomObject] @{ + ClusterObject = 'File Share Witness' + Name = 'SharePath' + IsReadOnly = 'False' + ParameterType = 'String' + Value = '\\FILE01\CLUSTER01' + } + ) + } + + Mock -CommandName 'Set-ClusterQuorum' -ParameterFilter { $FileShareWitness -eq '\\FILE01\CLUSTER01' } -MockWith { + } + + Context 'Validate Get-TargetResource method' { + + It 'Returns current configuration' { + + $Result = Get-TargetResource @TestParameter + + $Result.IsSingleInstance | Should Be $TestParameter.IsSingleInstance + $Result.Type | Should Be $TestParameter.Type + $Result.Resource | Should Be $TestParameter.Resource + } + } + + Context 'Validate Set-TargetResource method' { + + It 'Set the new configuration' { + + $Result = Set-TargetResource @TestParameter + + Assert-MockCalled -CommandName 'Set-ClusterQuorum' -ParameterFilter { $FileShareWitness -eq '\\FILE01\CLUSTER01' } -Times 1 + } + } + + Context 'Validate Test-TargetResource method' { + + It 'Check the current configuration' { + + $Result = Test-TargetResource @TestParameter + + $Result | Should Be $true + } + } + } +} + +Describe 'xClusterQuorum (NodeAndFileShareMajority / WS2016Prev)' { + + InModuleScope $ModuleName { + + $TestParameter = @{ + IsSingleInstance = 'Yes' + Type = 'NodeAndFileShareMajority' + Resource = '\\FILE01\CLUSTER01' + } + + Mock -CommandName 'Get-ClusterQuorum' -MockWith { + [PSCustomObject] @{ + Cluster = 'CLUSTER01' + QuorumType = 'Majority' + QuorumResource = [PSCustomObject] @{ + Name = 'File Share Witness' + OwnerGroup = 'Cluster Group' + ResourceType = [PSCustomObject] @{ + DisplayName = 'File Share Witness' + } + } + } + } + + Mock -CommandName 'Get-ClusterParameter' -ParameterFilter { $Name -eq 'SharePath' } -MockWith { + @( + [PSCustomObject] @{ + ClusterObject = 'File Share Witness' + Name = 'SharePath' + IsReadOnly = 'False' + ParameterType = 'String' + Value = '\\FILE01\CLUSTER01' + } + ) + } + + Mock -CommandName 'Set-ClusterQuorum' -ParameterFilter { $FileShareWitness -eq '\\FILE01\CLUSTER01' } -MockWith { + } + + Context 'Validate Get-TargetResource method' { + + It 'Returns current configuration' { + + $Result = Get-TargetResource @TestParameter + + $Result.IsSingleInstance | Should Be $TestParameter.IsSingleInstance + $Result.Type | Should Be $TestParameter.Type + $Result.Resource | Should Be $TestParameter.Resource + } + } + + Context 'Validate Set-TargetResource method' { + + It 'Set the new configuration' { + + $Result = Set-TargetResource @TestParameter + + Assert-MockCalled -CommandName 'Set-ClusterQuorum' -ParameterFilter { $FileShareWitness -eq '\\FILE01\CLUSTER01' } -Times 1 + } + } + + Context 'Validate Test-TargetResource method' { + + It 'Check the current configuration' { + + $Result = Test-TargetResource @TestParameter + + $Result | Should Be $true + } + } + } +} + + +## Test DiskOnly quorum type + +Describe 'xClusterQuorum (NodeAndDiskMajority / WS2012R2)' { + + InModuleScope $ModuleName { + + $TestParameter = @{ + IsSingleInstance = 'Yes' + Type = 'DiskOnly' + Resource = 'Witness' + } + + Mock -CommandName 'Get-ClusterQuorum' -MockWith { + [PSCustomObject] @{ + Cluster = 'CLUSTER01' + QuorumType = 'DiskOnly' + QuorumResource = [PSCustomObject] @{ + Name = 'Witness' + OwnerGroup = 'Cluster Group' + ResourceType = [PSCustomObject] @{ + DisplayName = 'Physical Disk' + } + } + } + } + + Mock -CommandName 'Set-ClusterQuorum' -ParameterFilter { $DiskOnly -eq 'Witness' } -MockWith { + } + + Context 'Validate Get-TargetResource method' { + + It 'Returns current configuration' { + + $Result = Get-TargetResource @TestParameter + + $Result.IsSingleInstance | Should Be $TestParameter.IsSingleInstance + $Result.Type | Should Be $TestParameter.Type + $Result.Resource | Should Be $TestParameter.Resource + } + } + + Context 'Validate Set-TargetResource method' { + + It 'Set the new configuration' { + + $Result = Set-TargetResource @TestParameter + + Assert-MockCalled -CommandName 'Set-ClusterQuorum' -ParameterFilter { $DiskOnly -eq 'Witness' } -Times 1 + } + } + + Context 'Validate Test-TargetResource method' { + + It 'Check the current configuration' { + + $Result = Test-TargetResource @TestParameter + + $Result | Should Be $true + } + } + } +} + +Describe 'xClusterQuorum (NodeAndDiskMajority / WS2016Prev)' { + + InModuleScope $ModuleName { + + $TestParameter = @{ + IsSingleInstance = 'Yes' + Type = 'DiskOnly' + Resource = 'Witness' + } + + Mock -CommandName 'Get-ClusterQuorum' -MockWith { + [PSCustomObject] @{ + Cluster = 'CLUSTER01' + QuorumType = 'DiskOnly' + QuorumResource = [PSCustomObject] @{ + Name = 'Witness' + OwnerGroup = 'Cluster Group' + ResourceType = [PSCustomObject] @{ + DisplayName = 'Physical Disk' + } + } + } + } + + Mock -CommandName 'Set-ClusterQuorum' -ParameterFilter { $DiskOnly -eq 'Witness' } -MockWith { + } + + Context 'Validate Get-TargetResource method' { + + It 'Returns current configuration' { + + $Result = Get-TargetResource @TestParameter + + $Result.IsSingleInstance | Should Be $TestParameter.IsSingleInstance + $Result.Type | Should Be $TestParameter.Type + $Result.Resource | Should Be $TestParameter.Resource + } + } + + Context 'Validate Set-TargetResource method' { + + It 'Set the new configuration' { + + $Result = Set-TargetResource @TestParameter + + Assert-MockCalled -CommandName 'Set-ClusterQuorum' -ParameterFilter { $DiskOnly -eq 'Witness' } -Times 1 + } + } + + Context 'Validate Test-TargetResource method' { + + It 'Check the current configuration' { + + $Result = Test-TargetResource @TestParameter + + $Result | Should Be $true + } + } + } +} + From d37c2e36d5b4d4a329f747c540e2d5bb7cd5f809 Mon Sep 17 00:00:00 2001 From: Troy Ault Date: Thu, 4 Aug 2016 15:04:28 -0400 Subject: [PATCH 4/9] Adding New Line to mof --- DSCResources/MSFT_xClusterQuorum/MSFT_xClusterQuorum.schema.mof | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DSCResources/MSFT_xClusterQuorum/MSFT_xClusterQuorum.schema.mof b/DSCResources/MSFT_xClusterQuorum/MSFT_xClusterQuorum.schema.mof index 9225793..ef0851d 100644 --- a/DSCResources/MSFT_xClusterQuorum/MSFT_xClusterQuorum.schema.mof +++ b/DSCResources/MSFT_xClusterQuorum/MSFT_xClusterQuorum.schema.mof @@ -6,4 +6,4 @@ class MSFT_xClusterQuorum : OMI_BaseResource [Write, ValueMap{"NodeMajority", "NodeAndDiskMajority", "NodeAndFileShareMajority", "DiskOnly"}, Values{"NodeMajority", "NodeAndDiskMajority", "NodeAndFileShareMajority", "DiskOnly"}] string Type; [Write] String Resource; -}; \ No newline at end of file +}; From 7399d1e18c9ef456da9563c15ca0fb46477e04cf Mon Sep 17 00:00:00 2001 From: Troy Ault Date: Thu, 4 Aug 2016 15:16:01 -0400 Subject: [PATCH 5/9] Adding Hashtable OutputType --- DSCResources/MSFT_xCluster/MSFT_xCluster.psm1 | 1 + 1 file changed, 1 insertion(+) diff --git a/DSCResources/MSFT_xCluster/MSFT_xCluster.psm1 b/DSCResources/MSFT_xCluster/MSFT_xCluster.psm1 index d922780..9995f1d 100644 --- a/DSCResources/MSFT_xCluster/MSFT_xCluster.psm1 +++ b/DSCResources/MSFT_xCluster/MSFT_xCluster.psm1 @@ -8,6 +8,7 @@ # function Get-TargetResource { + [OutputType([Hashtable])] param ( [parameter(Mandatory)] From 70a1bc96805e45dc47e186c09fc9efb6bfd16e50 Mon Sep 17 00:00:00 2001 From: Troy Ault Date: Thu, 4 Aug 2016 15:22:27 -0400 Subject: [PATCH 6/9] Fixing issues with return values and adding in changes requested from previous PR --- DSCResources/MSFT_xCluster/MSFT_xCluster.psm1 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/DSCResources/MSFT_xCluster/MSFT_xCluster.psm1 b/DSCResources/MSFT_xCluster/MSFT_xCluster.psm1 index 9995f1d..0c82fa8 100644 --- a/DSCResources/MSFT_xCluster/MSFT_xCluster.psm1 +++ b/DSCResources/MSFT_xCluster/MSFT_xCluster.psm1 @@ -52,6 +52,7 @@ function Get-TargetResource Name = $Name IPAddress = $address.Value } + $retvalue } # @@ -105,7 +106,7 @@ function Set-TargetResource New-Cluster -Name $Name -Node $env:COMPUTERNAME -StaticAddress $StaticIPAddress -NoStorage -Force - Start-Sleep -Seconds 60 + While (!(Get-Cluster)){Start-Sleep 5} Write-Verbose -Message "Created Cluster $Name" } @@ -159,6 +160,7 @@ function Set-TargetResource # function Test-TargetResource { + [OutputType([Boolean])] param ( [parameter(Mandatory)] From 56064db948ce08dc1b7703037232127621402454 Mon Sep 17 00:00:00 2001 From: Troy Ault Date: Thu, 4 Aug 2016 15:30:08 -0400 Subject: [PATCH 7/9] Correction in Get-Resource --- DSCResources/MSFT_xCluster/MSFT_xCluster.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DSCResources/MSFT_xCluster/MSFT_xCluster.psm1 b/DSCResources/MSFT_xCluster/MSFT_xCluster.psm1 index 0c82fa8..501e7b6 100644 --- a/DSCResources/MSFT_xCluster/MSFT_xCluster.psm1 +++ b/DSCResources/MSFT_xCluster/MSFT_xCluster.psm1 @@ -50,7 +50,7 @@ function Get-TargetResource $retvalue = @{ Name = $Name - IPAddress = $address.Value + StaticIPAddress = $address.Value } $retvalue } From dbbdcfc91cc77443a2397b844c1de5f8e06c344a Mon Sep 17 00:00:00 2001 From: Troy Ault Date: Wed, 10 Aug 2016 14:29:17 -0400 Subject: [PATCH 8/9] Callout that CloudWitness is not implemented --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 0a36085..6e9bec1 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,7 @@ Please check out common DSC Resources [contributing guidelines](https://github.c ### Unreleased * Added xClusterQuorum resource with options *NodeMajority*, *NodeAndDiskMajority*, *NodeAndFileShareMajority*, *DiskOnly* +* Currently does not implement cloudwitness for Windows 2016. * Added xClusterDisk resource ### 1.2.0.0 From f806a512f5ffd31ad9d8db9b7920358260f6d088 Mon Sep 17 00:00:00 2001 From: Katie Keim Date: Wed, 10 Aug 2016 13:47:00 -0700 Subject: [PATCH 9/9] Releasing version 1.5.0.0 --- README.md | 2 ++ appveyor.yml | 4 ++-- xFailOverCluster.psd1 | 7 +++++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 6e9bec1..fb23eac 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,8 @@ Please check out common DSC Resources [contributing guidelines](https://github.c ### Unreleased +### 1.5.0.0 + * Added xClusterQuorum resource with options *NodeMajority*, *NodeAndDiskMajority*, *NodeAndFileShareMajority*, *DiskOnly* * Currently does not implement cloudwitness for Windows 2016. * Added xClusterDisk resource diff --git a/appveyor.yml b/appveyor.yml index 1a512c8..2595a6f 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,7 +1,7 @@ #---------------------------------# # environment configuration # #---------------------------------# -version: 1.4.{build}.0 +version: 1.5.{build}.0 install: - git clone https://github.com/PowerShell/DscResource.Tests - ps: | @@ -38,7 +38,7 @@ deploy_script: # Creating project artifact $stagingDirectory = (Resolve-Path ..).Path $manifest = Join-Path $pwd "xFailOverCluster.psd1" - (Get-Content $manifest -Raw).Replace("1.4.0.0", $env:APPVEYOR_BUILD_VERSION) | Out-File $manifest + (Get-Content $manifest -Raw).Replace("1.5.0.0", $env:APPVEYOR_BUILD_VERSION) | Out-File $manifest $zipFilePath = Join-Path $stagingDirectory "$(Split-Path $pwd -Leaf).zip" Add-Type -assemblyname System.IO.Compression.FileSystem [System.IO.Compression.ZipFile]::CreateFromDirectory($pwd, $zipFilePath) diff --git a/xFailOverCluster.psd1 b/xFailOverCluster.psd1 index ec2d572..de85b15 100644 --- a/xFailOverCluster.psd1 +++ b/xFailOverCluster.psd1 @@ -3,7 +3,7 @@ @{ -ModuleVersion = '1.4.0.0' +ModuleVersion = '1.5.0.0' GUID = '026e7fd8-06dd-41bc-b373-59366ab18679' Author = 'Microsoft Corporation' @@ -30,7 +30,9 @@ PrivateData = @{ # IconUri = '' # ReleaseNotes of this module - ReleaseNotes = '* xClusterDisk: Fixed Test-TargetResource logic + ReleaseNotes = '* Added xClusterQuorum resource with options *NodeMajority*, *NodeAndDiskMajority*, *NodeAndFileShareMajority*, *DiskOnly* +* Currently does not implement cloudwitness for Windows 2016. +* Added xClusterDisk resource ' @@ -39,3 +41,4 @@ PrivateData = @{ } # End of PrivateData hashtable } +