Skip to content

Commit

Permalink
Refactor QlikNode resource and bootstrap rim node
Browse files Browse the repository at this point in the history
  • Loading branch information
ahaydon committed Oct 12, 2021
1 parent f9092ee commit 7a23b7d
Show file tree
Hide file tree
Showing 13 changed files with 819 additions and 314 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ modules/
.kitchen/
.package/
testresults/
*.pfx
234 changes: 234 additions & 0 deletions DSCClassResources/QlikNode.psm1
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
}
}
145 changes: 145 additions & 0 deletions Private/Bootstrap.psm1
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
}
}
Loading

0 comments on commit 7a23b7d

Please sign in to comment.