diff --git a/CHANGELOG.md b/CHANGELOG.md index 320a4ff5b..95bf036b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Update codecov.yml to support carry forward flags. - Updated pipelines files to latest from Sampler project. - Updated GitHub issue templates. + - Remove pipeline jobs `Test_Integration_SQL2016`, `Test_Integration_SQL2017`, + and `Test_Integration_SQL2019` and raplaced with a single job + `Test_Integration` ([issue #1713](https://github.com/dsccommunity/SqlServerDsc/issues/1713)). + - Update HQRM tests to run on the VM image `windows-2022`. + - Update unit tests to run on the VM image `windows-2022`. + - Update integration tests to run both on Windows Server 2019 and Windows + Server 2022 ([issue #1713](https://github.com/dsccommunity/SqlServerDsc/issues/1713)). - SqlSetup - The helper function `Connect-SqlAnalysis` was using `LoadWithPartial()` to load the assembly _Microsoft.AnalysisServices_. On a node where multiple @@ -55,13 +62,20 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - SqlRSSetup - Integration tests now install _Microsoft SQL Server 2019 Reporting Services_ ([issue #1717](https://github.com/dsccommunity/SqlServerDsc/issues/1717)). +- SqlRS + - Integration tests now configures _Microsoft SQL Server 2019 Reporting Services_. ### Fixed - SqlSetup - Fixed integration tests for SQL Server 2016 and SQL Server 2017. - SqlServerDsc.Common - - Fixed so that _CredScan_ no longer reports a password false-positive ([issue #1712](https://github.com/dsccommunity/SqlServerDsc/issues/1712)). + - Fixed so that _CredScan_ no longer reports a password false-positive + ([issue #1712](https://github.com/dsccommunity/SqlServerDsc/issues/1712)). +- SqlRS + - Fixed SSRS 2019 initialization ([issue #1509](https://github.com/dsccommunity/SqlServerDsc/issues/1509)). + - Fix a problem that did not correctly evaluate the `UseSSL` property against + the current state. ## [15.1.1] - 2021-02-12 @@ -2810,8 +2824,3 @@ in a future release. - xSQLServerSetup - xSQLServerFirewall - xSQLServerRSSecureConnectionLevel - - -### Changed -- Update VImage from 'windows-2019' to 'windows-2022' -- Remove Jobs Test_Integration_SQL2016, Test_Integration_SQL2017, Test_Integration_SQL2019 with a single job Test_Integration diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 5e5e22ec6..7b05381d1 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -116,7 +116,7 @@ stages: targetPath: '$(buildFolderName)/$(testResultFolderName)/' artifactName: $(testArtifactName) parallel: true - + - job: Test_Integration displayName: 'Integration' strategy: diff --git a/source/DSCResources/DSC_SqlRS/DSC_SqlRS.psm1 b/source/DSCResources/DSC_SqlRS/DSC_SqlRS.psm1 index fa7183d1a..3eab32b9c 100644 --- a/source/DSCResources/DSC_SqlRS/DSC_SqlRS.psm1 +++ b/source/DSCResources/DSC_SqlRS/DSC_SqlRS.psm1 @@ -160,7 +160,7 @@ function Get-TargetResource .PARAMETER SuppressRestart Reporting Services need to be restarted after initialization or settings change. If this parameter is set to $true, Reporting Services - will not be restarted, even after initialisation. + will not be restarted, even after initialization. .NOTES To find out the parameter names for the methods in the class @@ -463,6 +463,23 @@ function Set-TargetResource Invoke-RsCimMethod @invokeRsCimMethodParameters + <# + When initializing SSRS 2019, the call to InitializeReportServer + always fails, even if IsInitialized flag is $false. + It also seems that simply restarting SSRS at this point initializes + it. + + We will ignore $SuppressRestart here. + #> + if ($reportingServicesData.SqlVersion -ge 15) + { + Write-Verbose -Message $script:localizedData.RestartToFinishInitialization + + Restart-ReportingServicesService -InstanceName $InstanceName -WaitTime 30 + + $restartReportingService = $false + } + $reportingServicesData = Get-ReportingServicesData -InstanceName $InstanceName <# @@ -473,6 +490,8 @@ function Set-TargetResource #> if ( -not $reportingServicesData.Configuration.IsInitialized ) { + $restartReportingService = $true + $invokeRsCimMethodParameters = @{ CimInstance = $reportingServicesData.Configuration MethodName = 'InitializeReportServer' @@ -484,10 +503,12 @@ function Set-TargetResource Invoke-RsCimMethod @invokeRsCimMethodParameters } - if ( $PSBoundParameters.ContainsKey('UseSsl') -and $UseSsl -ne $currentConfig.UseSsl ) + if ( $PSBoundParameters.ContainsKey('UseSsl') -and $UseSsl -ne $reportingServicesData.Configuration.SecureConnectionLevel ) { Write-Verbose -Message "Changing value for using SSL to '$UseSsl'." + $restartReportingService = $true + $invokeRsCimMethodParameters = @{ CimInstance = $reportingServicesData.Configuration MethodName = 'SetSecureConnectionLevel' @@ -1012,4 +1033,3 @@ function Invoke-RsCimMethod return $invokeCimMethodResult } - diff --git a/source/DSCResources/DSC_SqlRS/en-US/DSC_SqlRS.strings.psd1 b/source/DSCResources/DSC_SqlRS/en-US/DSC_SqlRS.strings.psd1 index 2d0d26a5e..07dd9a79e 100644 --- a/source/DSCResources/DSC_SqlRS/en-US/DSC_SqlRS.strings.psd1 +++ b/source/DSCResources/DSC_SqlRS/en-US/DSC_SqlRS.strings.psd1 @@ -4,4 +4,5 @@ ConvertFrom-StringData @' TestFailedAfterSet = Test-TargetResource function returned false when Set-TargetResource function verified the desired state. This indicates that the Set-TargetResource did not correctly set set the desired state, or that the function Test-TargetResource does not correctly evaluate the desired state. ReportingServicesNotFound = SQL Reporting Services instance '{0}' does not exist. GetConfiguration = Get the current reporting services configuration for the instance '{0}'. + RestartToFinishInitialization = Restarting Reporting Services to finish initialization. '@ diff --git a/tests/Integration/DSC_SqlRS.Integration.Tests.ps1 b/tests/Integration/DSC_SqlRS.Integration.Tests.ps1 index 2c3796ecf..e4b28ca55 100644 --- a/tests/Integration/DSC_SqlRS.Integration.Tests.ps1 +++ b/tests/Integration/DSC_SqlRS.Integration.Tests.ps1 @@ -1,7 +1,7 @@ Import-Module -Name (Join-Path -Path $PSScriptRoot -ChildPath '..\TestHelpers\CommonTestHelper.psm1') # Not currently run for SQL Server 2019 -if (-not (Test-BuildCategory -Type 'Integration' -Category @('Integration_SQL2016','Integration_SQL2017'))) +if (-not (Test-BuildCategory -Type 'Integration' -Category @('Integration_SQL2016', 'Integration_SQL2017', 'Integration_SQL2019'))) { return } @@ -30,7 +30,11 @@ $script:testEnvironment = Initialize-TestEnvironment ` to run the correct tests depending of what version of SQL Server is being tested in the current job. #> -if (Test-ContinuousIntegrationTaskCategory -Category 'Integration_SQL2017') +if (Test-ContinuousIntegrationTaskCategory -Category 'Integration_SQL2019') +{ + $script:sqlVersion = '150' +} +elseif (Test-ContinuousIntegrationTaskCategory -Category 'Integration_SQL2017') { $script:sqlVersion = '140' } @@ -128,7 +132,10 @@ try } It 'Should be able to access the ReportServer site without any error' { - if($script:sqlVersion -eq '140') + # Wait for 1 minute for the ReportServer to be ready. + Start-Sleep -Seconds 30 + + if ($script:sqlVersion -in @('140', '150')) { # SSRS 2017 and 2019 do not support multiple instances $reportServerUri = 'http://{0}/ReportServer' -f $env:COMPUTERNAME @@ -158,7 +165,37 @@ try } It 'Should be able to access the Reports site without any error' { - if($script:sqlVersion -eq '140') + if ($script:sqlVersion -in @('140', '150')) + { + # SSRS 2017 and 2019 do not support multiple instances + $reportsUri = 'http://{0}/Reports' -f $env:COMPUTERNAME + } + else + { + $reportsUri = 'http://{0}/Reports_{1}' -f $env:COMPUTERNAME, $ConfigurationData.AllNodes.InstanceName + } + + try + { + $webRequestReportServer = Invoke-WebRequest -Uri $reportsUri -UseDefaultCredentials + # if the request finishes successfully this should return status code 200. + $webRequestStatusCode = $webRequestReportServer.StatusCode -as [int] + } + catch + { + <# + If the request generated an exception i.e. "HTTP Error 503. The service is unavailable." + we can pull the status code from the Exception.Response property. + #> + $webRequestResponse = $_.Exception.Response + $webRequestStatusCode = $webRequestResponse.StatusCode -as [int] + } + + $webRequestStatusCode | Should -BeExactly 200 + } + + It 'Should be able to access the Reports site without any error' { + if ($script:sqlVersion -in @('140', '150')) { # SSRS 2017 and 2019 do not support multiple instances $reportsUri = 'http://{0}/Reports' -f $env:COMPUTERNAME @@ -240,7 +277,7 @@ try as this without testing for the correct error message on purpose. #> It 'Should not be able to access the ReportServer site and throw an error message' { - if($script:sqlVersion -eq '140') + if ($script:sqlVersion -in @('140', '150')) { # SSRS 2017 and 2019 do not support multiple instances $reportServerUri = 'http://{0}/ReportServer' -f $env:COMPUTERNAME @@ -300,7 +337,7 @@ try } It 'Should be able to access the ReportServer site without any error' { - if($script:sqlVersion -eq '140') + if ($script:sqlVersion -in @('140', '150')) { # SSRS 2017 and 2019 do not support multiple instances $reportServerUri = 'http://{0}/ReportServer' -f $env:COMPUTERNAME @@ -328,6 +365,36 @@ try $webRequestStatusCode | Should -BeExactly 200 } + + It 'Should be able to access the Reports site without any error' { + if ($script:sqlVersion -in @('140', '150')) + { + # SSRS 2017 and 2019 do not support multiple instances + $reportsUri = 'http://{0}/Reports' -f $env:COMPUTERNAME + } + else + { + $reportsUri = 'http://{0}/Reports_{1}' -f $env:COMPUTERNAME, $ConfigurationData.AllNodes.InstanceName + } + + try + { + $webRequestReportServer = Invoke-WebRequest -Uri $reportsUri -UseDefaultCredentials + # if the request finishes successfully this should return status code 200. + $webRequestStatusCode = $webRequestReportServer.StatusCode -as [int] + } + catch + { + <# + If the request generated an exception i.e. "HTTP Error 503. The service is unavailable." + we can pull the status code from the Exception.Response property. + #> + $webRequestResponse = $_.Exception.Response + $webRequestStatusCode = $webRequestResponse.StatusCode -as [int] + } + + $webRequestStatusCode | Should -BeExactly 200 + } } $configurationName = "$($script:dscResourceName)_StopReportingServicesInstance_Config" diff --git a/tests/Integration/DSC_SqlRS.config.ps1 b/tests/Integration/DSC_SqlRS.config.ps1 index be21d9486..b28ad7b69 100644 --- a/tests/Integration/DSC_SqlRS.config.ps1 +++ b/tests/Integration/DSC_SqlRS.config.ps1 @@ -21,19 +21,16 @@ else { # SQL2019 $instanceName = 'SSRS' - $isoImageName = 'SQL2019.iso' } elseif($script:sqlVersion -eq '140') { # SQL2017 $instanceName = 'SSRS' - $isoImageName = 'SQL2017.iso' } else { # SQL2016 $instanceName = 'DSCRS2016' - $isoImageName = 'SQL2016.iso' } $ConfigurationData = @{ @@ -54,7 +51,7 @@ else SuppressReboot = $true # Make sure we don't reboot during testing. ForceReboot = $false - ImagePath = "$env:TEMP\$isoImageName" + ImagePath = "$env:TEMP\SQL2016.iso" DriveLetter = $mockIsoMediaDriveLetter DatabaseServerName = $env:COMPUTERNAME @@ -95,7 +92,7 @@ Configuration DSC_SqlRS_CreateDependencies_Config Ensure = 'Present' } - if($script:sqlVersion -eq '130') + if ($script:sqlVersion -eq '130') { MountImage 'MountIsoMedia' { @@ -142,7 +139,7 @@ Configuration DSC_SqlRS_CreateDependencies_Config DSC_SqlRSSetup.Integration.Tests.ps1 will have installed SSRS 2017 or 2019. We just need to start SSRS. #> - elseif($script:sqlVersion -in @('150','140')) + elseif ($script:sqlVersion -in @('140', '150')) { Service 'StartReportingServicesInstance' { diff --git a/tests/Integration/README.md b/tests/Integration/README.md index 8cffd6227..d19d12347 100644 --- a/tests/Integration/README.md +++ b/tests/Integration/README.md @@ -282,13 +282,20 @@ names `'Integration_SQL2016'`, `'Integration_SQL2017'` and `'Integration_SQL2019 When running integration tests for SQL Server 2016 the integration tests will install the following instances and leave it on the build server for other integration tests to use. -When running integration tests for SQL Server 2017 and 2019, the Reporting Services -instance installed by the resource SqlRSSetup is started, and then stopped -again after the integration tests has run. +When running integration tests the Reporting Services instance is started, and +then stopped again after the integration tests has run. + +For `'Integration_SQL2016'`: + +Instance | Feature | Description +--- | --- | --- +DSCRS2016 | RS | The Reporting Services 2016 is initialized, and in a working state. + +For `'Integration_SQL2017'` and `'Integration_SQL2019'`: Instance | Feature | Description --- | --- | --- -DSCRS2016 | RS | The Reporting Services is initialized, and in a working state. +SSRS | - | The Reporting Services (2017 or 2019) is initialized, and in a working state. >**Note:** The Reporting Services service is stopped to save memory on the build >worker. diff --git a/tests/Unit/DSC_SqlRS.Tests.ps1 b/tests/Unit/DSC_SqlRS.Tests.ps1 index f31ac4bcf..5392c8cf5 100644 --- a/tests/Unit/DSC_SqlRS.Tests.ps1 +++ b/tests/Unit/DSC_SqlRS.Tests.ps1 @@ -333,7 +333,8 @@ try Version = 15 } ) - foreach($sqlVersion in $sqlVersions) + + foreach ($sqlVersion in $sqlVersions) { Context "When the system is not in the desired state ($($sqlVersion.VersionName))" { Context "When configuring a named instance that are not initialized ($($sqlVersion.VersionName))" { @@ -411,7 +412,15 @@ try Assert-MockCalled -CommandName Get-CimInstance -Exactly -Times 1 -Scope It Assert-MockCalled -CommandName Invoke-Sqlcmd -Exactly -Times 2 -Scope It - Assert-MockCalled -CommandName Restart-ReportingServicesService -Exactly -Times 1 -Scope It + + if ($sqlVersion.Version -eq 15) + { + Assert-MockCalled -CommandName Restart-ReportingServicesService -Exactly -Times 2 -Scope It + } + else + { + Assert-MockCalled -CommandName Restart-ReportingServicesService -Exactly -Times 1 -Scope It + } } Context 'When there is no Reporting Services instance after Set-TargetResource has been called' { @@ -437,6 +446,107 @@ try } } + if ($sqlVersion.Version -eq 15) + { + Context "When configuring a named instance of $($sqlVersion.VersionName) that are not initialized" { + BeforeAll { + $script:alreadyCalledGetReportingServicesData = $false + $script:mockDynamicIsInitialized = $false + $script:mockDynamicSecureConnectionLevel = $false + + Mock -CommandName Get-ReportingServicesData -MockWith { + if ($script:alreadyCalledGetReportingServicesData) + { + $script:mockDynamicIsInitialized = $true + } + else + { + $script:alreadyCalledGetReportingServicesData = $true + } + + return @{ + Configuration = New-Object -TypeName Microsoft.Management.Infrastructure.CimInstance -ArgumentList @( + 'MSReportServer_ConfigurationSetting' + 'root/Microsoft/SQLServer/ReportServer/RS_SQL2019/v15/Admin' + ) | Add-Member -MemberType NoteProperty -Name 'DatabaseServerName' -Value "$mockReportingServicesDatabaseServerName\$mockReportingServicesDatabaseNamedInstanceName" -PassThru | + Add-Member -MemberType NoteProperty -Name 'IsInitialized' -Value $script:mockDynamicIsInitialized -PassThru | + Add-Member -MemberType NoteProperty -Name 'InstanceName' -Value $mockNamedInstanceName -PassThru | + Add-Member -MemberType NoteProperty -Name 'VirtualDirectoryReportServer' -Value $mockVirtualDirectoryReportServerName -PassThru | + Add-Member -MemberType NoteProperty -Name 'VirtualDirectoryReportManager' -Value $mockVirtualDirectoryReportManagerName -PassThru | + Add-Member -MemberType NoteProperty -Name 'SecureConnectionLevel' -Value $script:mockDynamicSecureConnectionLevel -PassThru -Force + ReportsApplicationName = 'ReportServerWebApp' + SqlVersion = $sqlVersion.Version + } + } + + Mock -CommandName Test-TargetResource -MockWith { + return $true + } + + $defaultParameters = @{ + InstanceName = $mockNamedInstanceName + DatabaseServerName = $mockReportingServicesDatabaseServerName + DatabaseInstanceName = $mockReportingServicesDatabaseNamedInstanceName + UseSsl = $false + } + } + + BeforeEach { + Mock -CommandName Get-CimInstance ` + -MockWith $mockGetCimInstance_Language ` + -ParameterFilter $mockGetCimInstance_OperatingSystem_ParameterFilter + } + + It 'Should configure Reporting Service without throwing an error' { + { Set-TargetResource @defaultParameters } | Should -Not -Throw + + Assert-MockCalled -CommandName Invoke-RsCimMethod -ParameterFilter { + $MethodName -eq 'SetSecureConnectionLevel' + } -Exactly -Times 0 -Scope It + + Assert-MockCalled -CommandName Invoke-RsCimMethod -ParameterFilter { + $MethodName -eq 'RemoveURL' + } -Exactly -Times 0 -Scope It + + Assert-MockCalled -CommandName Invoke-RsCimMethod -ParameterFilter { + $MethodName -eq 'InitializeReportServer' + } -Exactly -Times 0 -Scope It + + Assert-MockCalled -CommandName Invoke-RsCimMethod -ParameterFilter { + $MethodName -eq 'SetDatabaseConnection' + } -Exactly -Times 1 -Scope It + + Assert-MockCalled -CommandName Invoke-RsCimMethod -ParameterFilter { + $MethodName -eq 'GenerateDatabaseRightsScript' + } -Exactly -Times 1 -Scope It + + Assert-MockCalled -CommandName Invoke-RsCimMethod -ParameterFilter { + $MethodName -eq 'GenerateDatabaseCreationScript' + } -Exactly -Times 1 -Scope It + + Assert-MockCalled -CommandName Invoke-RsCimMethod -ParameterFilter { + $MethodName -eq 'SetVirtualDirectory' -and $Arguments.Application -eq $mockReportServerApplicationName + } -Exactly -Times 1 -Scope It + + Assert-MockCalled -CommandName Invoke-RsCimMethod -ParameterFilter { + $MethodName -eq 'SetVirtualDirectory' -and $Arguments.Application -eq $mockReportsApplicationName + } -Exactly -Times 1 -Scope It + + Assert-MockCalled -CommandName Invoke-RsCimMethod -ParameterFilter { + $MethodName -eq 'ReserveUrl' -and $Arguments.Application -eq $mockReportServerApplicationName + } -Exactly -Times 1 -Scope It + + Assert-MockCalled -CommandName Invoke-RsCimMethod -ParameterFilter { + $MethodName -eq 'ReserveUrl' -and $Arguments.Application -eq $mockReportsApplicationName + } -Exactly -Times 1 -Scope It + + Assert-MockCalled -CommandName Get-CimInstance -Exactly -Times 1 -Scope It + Assert-MockCalled -CommandName Invoke-Sqlcmd -Exactly -Times 2 -Scope It + Assert-MockCalled -CommandName Restart-ReportingServicesService -Exactly -Times 1 -Scope It + } + } + } + Context "When configuring a named instance that are already initialized ($($sqlVersion.VersionName))" { BeforeAll { $mockDynamicIsInitialized = $true @@ -697,8 +807,15 @@ try Assert-MockCalled -CommandName Get-CimInstance -Exactly -Times 1 -Scope It Assert-MockCalled -CommandName Invoke-Sqlcmd -Exactly -Times 2 -Scope It - Assert-MockCalled -CommandName Restart-ReportingServicesService -Exactly -Times 1 -Scope It - } + + if ($sqlVersion.Version -eq 15) + { + Assert-MockCalled -CommandName Restart-ReportingServicesService -Exactly -Times 2 -Scope It + } + else + { + Assert-MockCalled -CommandName Restart-ReportingServicesService -Exactly -Times 1 -Scope It + } } } } }