-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor QlikNode resource and bootstrap rim node
- Loading branch information
Showing
13 changed files
with
819 additions
and
314 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,3 +4,4 @@ modules/ | |
.kitchen/ | ||
.package/ | ||
testresults/ | ||
*.pfx |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,234 @@ | ||
$ProjectRoot = Split-Path $PSScriptRoot -Parent | ||
Import-Module (Join-Path $ProjectRoot -ChildPath 'Private' | Join-Path -ChildPath 'Common.psm1') -Force | ||
Import-Module (Join-Path $ProjectRoot -ChildPath 'Private' | Join-Path -ChildPath 'Bootstrap.psm1') -Force | ||
|
||
enum Ensure { | ||
Absent | ||
Present | ||
} | ||
|
||
[DscResource()] | ||
class QlikNode{ | ||
|
||
[DscProperty(Key)] | ||
[string] | ||
$HostName | ||
|
||
[DscProperty()] | ||
[string] | ||
$Name | ||
|
||
[DscProperty()] | ||
[string] | ||
$NodePurpose | ||
|
||
[DscProperty()] | ||
[hashtable] | ||
$CustomProperties | ||
|
||
[DscProperty()] | ||
[string[]] | ||
$Tags | ||
|
||
[DscProperty()] | ||
[Nullable[bool]] | ||
$Engine | ||
|
||
[DscProperty()] | ||
[Nullable[bool]] | ||
$Proxy | ||
|
||
[DscProperty()] | ||
[Nullable[bool]] | ||
$Scheduler | ||
|
||
[DscProperty()] | ||
[Nullable[bool]] | ||
$Printing | ||
|
||
[DscProperty()] | ||
[Nullable[bool]] | ||
$Failover | ||
|
||
[DscProperty(Mandatory)] | ||
[Ensure] | ||
$Ensure | ||
|
||
[void] Set() { | ||
$item = Get-QlikNode -full -filter "hostName eq '$($this.HostName)'" | ||
$present = $null -ne $item | ||
$Bootstrap = $null | ||
|
||
if ($this.ensure -eq [Ensure]::Present) { | ||
$params = @{ | ||
engineEnabled = $this.Engine | ||
proxyEnabled = $this.Proxy | ||
schedulerEnabled = $this.Scheduler | ||
printingEnabled = $this.Printing | ||
Failover = $this.Failover | ||
} | ||
if ($this.Name) { $params.Add("Name", $this.Name) } | ||
if ($this.NodePurpose) { $params.Add("NodePurpose", $this.NodePurpose) } | ||
|
||
$props = ConfigurePropertiesAndTags($this) | ||
if ($props.CustomProperties) { $params.Add("CustomProperties", $props.CustomProperties)} | ||
if ($props.Tags) { $params.Add("Tags", $props.Tags)} | ||
|
||
if ($present) { | ||
if (-not $this.hasProperties($item)) { | ||
Update-QlikNode -id $item.id @params | ||
} | ||
|
||
if ((Get-Service QlikSenseRepositoryService).Status -ne 'Running') { | ||
$Bootstrap = Start-SenseBootstrap -Service Repository | ||
} | ||
else { | ||
$counter = 0 | ||
while (Get-QlikServiceStatus -full -filter "serverNodeConfiguration.id eq $($item.id) and serviceType eq Repository and serviceState eq NoCommunication") { | ||
$counter++ | ||
if ($counter -gt 20) { throw "Repository service status is NoCommunication" } | ||
Start-Sleep -Seconds 15 | ||
} | ||
} | ||
|
||
if ($state = Get-QlikServiceStatus -full -filter "serverNodeConfiguration.id eq $($item.id) and serviceType eq Repository and serviceState ne Running") { | ||
Write-Verbose "Repository service status is $($state.serviceState)" | ||
$password = Invoke-QlikGet "/qrs/servernoderegistration/start/$($item.id)" | ||
if ($password) { | ||
Write-Verbose "Unlocking certificates on node" | ||
$postParams = @{__pwd = "$password" } | ||
Invoke-WebRequest -Uri "http://localhost:4570/certificateSetup" -Method Post -Body $postParams -UseBasicParsing > $null | ||
} | ||
|
||
if ($Bootstrap) { | ||
$Bootstrap.WaitForExit() | ||
if ($Bootstrap.ExitCode -ne 0) { | ||
throw "Bootstrap exited with status $($Bootstrap.ExitCode)" | ||
} | ||
} | ||
} | ||
} | ||
else { | ||
if ((Get-Service QlikSenseRepositoryService).Status -ne 'Running') { | ||
$Bootstrap = Start-SenseBootstrap -Service Repository | ||
} | ||
Register-QlikNode -hostName $this.HostName @params | ||
if ($Bootstrap) { | ||
$Bootstrap.WaitForExit() | ||
if ($Bootstrap.ExitCode -ne 0) { | ||
throw "Bootstrap exited with status $($Bootstrap.ExitCode)" | ||
} | ||
} | ||
} | ||
} | ||
else { | ||
Remove-QlikNode $item.id | ||
} | ||
} | ||
|
||
[bool] Test() { | ||
$item = Get-QlikNode -full -filter "hostName eq '$($this.HostName)'" | ||
$present = $null -ne $item | ||
|
||
if ($this.ensure -eq [Ensure]::Present) { | ||
if ($present) { | ||
if ($this.hasProperties($item)) { | ||
if ($state = Get-QlikServiceStatus -full -filter "serverNodeConfiguration.id eq $($item.id) and serviceType eq Repository and serviceState ne Running") { | ||
Write-Verbose "Repository service status is $($state.serviceState)" | ||
return $false | ||
} | ||
|
||
Write-Verbose "Node with hostname of '$($this.HostName)' is present and correct" | ||
return $true | ||
} else { | ||
return $false | ||
} | ||
} else { | ||
Write-Verbose "Node not found with hostname of '$($this.HostName)', but should be present" | ||
return $false | ||
} | ||
} else { | ||
if ($present) { | ||
Write-Verbose "Node found with hostname of '$($this.HostName)', but should be absent" | ||
return $false | ||
} | ||
else { | ||
Write-Verbose "Node with hostname of '$($this.HostName)' is in desired state of absent" | ||
return $true | ||
} | ||
} | ||
} | ||
|
||
[QlikNode] Get() { | ||
$item = Get-QlikNode -raw -full -filter "hostName eq '$($this.HostName)'" | ||
$present = $null -ne $item | ||
|
||
if ($present) { | ||
$this.NodePurpose = $item.NodePurpose | ||
$this.CustomProperties = $item.CustomProperties | ||
$this.Tags = $item.Tags | ||
$this.Engine = $item.EngineEnabled | ||
$this.Proxy = $item.ProxyEnabled | ||
$this.Scheduler = $item.SchedulerEnabled | ||
$this.Printing = $item.PrintingEnabled | ||
$this.Failover = $item.FailoverCandidate | ||
} | ||
|
||
return $this | ||
} | ||
|
||
[bool] hasProperties($item) { | ||
if (! (CompareProperties $this $item @( 'NodePurpose', 'Tags', 'Name' ))) { | ||
return $false | ||
} | ||
|
||
if ($this.CustomProperties) { | ||
foreach ($defined in $this.CustomProperties) { | ||
$val = $defined.Split("=") | ||
$found = $false | ||
foreach ($exists in $item.customProperties) { | ||
if ($exists.definition.name -eq $val[0]) { | ||
if ($val[1] -eq "null" -Or $val[1] -ne $exists.value) { | ||
Write-Verbose "Test-HasProperties: Custom property value - $($val[0])=$($exists.value) does not match desired state - $($val[1])" | ||
return $false | ||
} | ||
else { | ||
$found = $true | ||
} | ||
} | ||
} | ||
|
||
if (-not $found) { | ||
return $false | ||
} | ||
} | ||
} | ||
|
||
if ($null -ne $this.Engine -and $item.EngineEnabled -ne $this.Engine) { | ||
Write-Verbose "Test-HasProperties: Engine property value - $($item.EngineEnabled) does not match desired state - $($this.Engine)" | ||
return $false | ||
} | ||
|
||
if ($null -ne $this.Proxy -and $item.ProxyEnabled -ne $this.Proxy) { | ||
Write-Verbose "Test-HasProperties: Proxy property value - $($item.ProxyEnabled) does not match desired state - $($this.Proxy)" | ||
return $false | ||
} | ||
|
||
if ($null -ne $this.Scheduler -and $item.SchedulerEnabled -ne $this.Scheduler) { | ||
Write-Verbose "Test-HasProperties: Scheduler property value - $($item.SchedulerEnabled) does not match desired state - $($this.Scheduler)" | ||
return $false | ||
} | ||
|
||
if ($null -ne $this.Printing -and $item.PrintingEnabled -ne $this.Printing) { | ||
Write-Verbose "Test-HasProperties: Printing property value - $($item.PrintingEnabled) does not match desired state - $($this.Printing)" | ||
return $false | ||
} | ||
|
||
if ($null -ne $this.Failover -and $item.failover -ne $this.Failover) { | ||
Write-Verbose "Test-HasProperties: Failover property value - $($item.Failover) does not match desired state - $($this.Failover)" | ||
return $false | ||
} | ||
|
||
return $true | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
function Get-ServicePath { | ||
[CmdletBinding()] | ||
[OutputType([System.IO.FileInfo])] | ||
param ( | ||
[Parameter( | ||
Position = 0, | ||
ValueFromPipelineByPropertyName = $true | ||
)] | ||
[string[]] | ||
$Name, | ||
|
||
[Parameter()] | ||
[string] | ||
$Filter = 'Name like "{0}"' | ||
) | ||
|
||
begin { | ||
$Query = $null | ||
$Names = @() | ||
} | ||
|
||
process { | ||
if ($input) { | ||
$Names += $Name | ||
} | ||
elseif ($Name) { | ||
$Query = $Filter -f ($Name -replace '\*', '%') | ||
Write-Verbose "WQL = $Query" | ||
} | ||
} | ||
|
||
end { | ||
$Services = @(Get-CimInstance -ClassName Win32_Service -Property PathName -Filter $Query) | ||
Write-Verbose "Found $($Services.Count) services" | ||
if ($Names.Count) { | ||
$Services = @($Services | Where-Object Name -in $Names) | ||
Write-Verbose "Filtering services, input=$($Names.Count), output=$($Services.Count)" | ||
} | ||
foreach ($PathName in $Services.PathName) { | ||
$PathMatch = ($PathName | Select-String '"([^\"]*)"|[^\s]*').Matches[0] | ||
if ($PathMatch.Groups[1].Success) { | ||
$Path = $PathMatch.Groups[1].Value | ||
} | ||
else { | ||
$Path = $PathMatch.Groups[0].Value | ||
} | ||
Write-Verbose "Service path resolved to $Path" | ||
[System.IO.FileInfo]$Path | ||
} | ||
} | ||
} | ||
|
||
function Start-SenseBootstrap { | ||
[CmdletBinding()] | ||
param ( | ||
[Parameter( | ||
Mandatory = $true, | ||
Position = 0, | ||
ValueFromPipelineByPropertyName = $true | ||
)] | ||
[Alias('Name')] | ||
[ValidateSet('Repository', 'Proxy', 'Scheduler')] | ||
[string] | ||
$Service, | ||
|
||
[Parameter()] | ||
[switch] | ||
$IsCentral, | ||
|
||
[Parameter()] | ||
[switch] | ||
$RestoreHostname | ||
) | ||
|
||
process { | ||
Write-Verbose "Starting bootstrap of $Service service" | ||
$Arguments = '-bootstrap -standalone' | ||
if ($RestoreHostname.IsPresent) { | ||
$Arguments += '-restorehostname' | ||
} | ||
if ($IsCentral.IsPresent) { | ||
$Arguments += '-iscentral' | ||
} | ||
|
||
$ServiceName = "QlikSense${Service}Service" | ||
$ServicePath = Get-ServicePath -Name $ServiceName | ||
|
||
if ((Get-Service $ServiceName).Status -eq 'Running') { | ||
Write-Verbose "Stopping service $ServiceName" | ||
Stop-Service -Name $ServiceName -Force | ||
} | ||
if ($ServiceName -ne 'QlikSenseRepositoryService') { | ||
Start-Service -Name QlikSenseRepositoryService | ||
} | ||
Write-Verbose 'Starting service QlikSenseServiceDispatcher' | ||
Start-Service -Name QlikSenseServiceDispatcher | ||
|
||
$process = RunShellCommand -FileName $ServicePath -Arguments $Arguments | ||
$lineCount = 0 | ||
|
||
while ($null -ne ($line = $process.StandardOutput.ReadLine())) { | ||
if (! $line) { | ||
continue | ||
} | ||
|
||
Write-Verbose $line | ||
if ($line -match 'Waiting for certificates to be installed') { | ||
return $process | ||
} | ||
|
||
$lineCount++ | ||
} | ||
|
||
if ($process.ExitCode -ne 0) { | ||
Write-Error "Bootstrap failed with Exitcode: $($process.ExitCode)" | ||
} | ||
Write-Verbose "Bootstrap job completed" | ||
} | ||
} | ||
|
||
function RunShellCommand { | ||
param ( | ||
$FileName, | ||
$Arguments | ||
) | ||
|
||
process { | ||
$startInfo = New-Object System.Diagnostics.ProcessStartInfo | ||
$startInfo.UseShellExecute = $false | ||
$startInfo.FileName = $FileName | ||
$startInfo.WorkingDirectory = ([System.IO.FileInfo]$FileName).DirectoryName | ||
$startInfo.CreateNoWindow = $true | ||
$startInfo.Arguments = $Arguments | ||
$startInfo.RedirectStandardOutput = $true | ||
$startInfo.RedirectStandardError = $true | ||
$startInfo.RedirectStandardInput = $true | ||
|
||
$process = New-Object System.Diagnostics.Process | ||
$process.StartInfo = $startInfo | ||
|
||
Write-Verbose "Starting `"$($startInfo.FileName)`" with arguments ($($startInfo.Arguments))" | ||
$process.Start() | Out-Null | ||
$process | ||
} | ||
} |
Oops, something went wrong.