mirror of
https://github.com/Icinga/icinga-powershell-framework.git
synced 2025-12-20 23:00:35 -05:00
Merge pull request #454 from Icinga:fix/jea_background_daemon_handling
Fix: Background daemon not working in JEA context The current implementation of the Icinga for Windows background daemon was not working entirely by using JEa profiles. We fixed the behavior by resolving a problem within our JEA profile catalog builder as well as other factors causing issues. All background daemons will now work properly inside the JEA context.
This commit is contained in:
commit
ca1eab8b00
13 changed files with 149 additions and 35 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -6,6 +6,7 @@ cache/*
|
|||
.vscode/
|
||||
.vs/
|
||||
*.log
|
||||
*.pfx
|
||||
|
||||
# JEA
|
||||
RoleCapabilities/IcingaForWindows.psrc
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ Released closed milestones can be found on [GitHub](https://github.com/Icinga/ic
|
|||
* [#449](https://github.com/Icinga/icinga-powershell-framework/pull/449) Fixes unhandled exception while importing modules during `Install-IcingaComponent` process, because of possible missing dependencies
|
||||
* [#451](https://github.com/Icinga/icinga-powershell-framework/pull/451) Fixes PowerShell being unable to enter JEA context if only the Framework is installed and removes the `|` from plugin output, in case a JEA error is thrown that check commands are not present
|
||||
* [#452](https://github.com/Icinga/icinga-powershell-framework/pull/452) Fixes unhandled `true` output on the console while running the installer
|
||||
* [#454](https://github.com/Icinga/icinga-powershell-framework/pull/454) Fixes JEA catalog compiler and background daemon execution in JEA context
|
||||
|
||||
### Enhancements
|
||||
|
||||
|
|
|
|||
|
|
@ -172,8 +172,6 @@ function New-IcingaForWindowsComponent()
|
|||
Add-Content -Path (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('daemon\{0}.psm1', $DaemonEntry))) -Value ' # This is your main daemon function. Add your code inside the WHILE() loop which is executed once the daemon is loaded.';
|
||||
Add-Content -Path (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('daemon\{0}.psm1', $DaemonEntry))) -Value ' # Also check the developer guide for further details: https://icinga.com/docs/icinga-for-windows/latest/doc/900-Developer-Guide/10-Custom-Daemons/';
|
||||
Add-Content -Path (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('daemon\{0}.psm1', $DaemonEntry))) -Value '';
|
||||
Add-Content -Path (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('daemon\{0}.psm1', $DaemonEntry))) -Value ' Use-Icinga -LibOnly -Daemon;';
|
||||
Add-Content -Path (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('daemon\{0}.psm1', $DaemonEntry))) -Value '';
|
||||
Add-Content -Path (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('daemon\{0}.psm1', $DaemonEntry))) -Value ' while ($TRUE) {';
|
||||
Add-Content -Path (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('daemon\{0}.psm1', $DaemonEntry))) -Value ' # Add your daemon code within this loop';
|
||||
Add-Content -Path (Join-Path -Path $ModuleDir -ChildPath ([string]::Format('daemon\{0}.psm1', $DaemonEntry))) -Value ' Start-Sleep -Seconds 1;';
|
||||
|
|
|
|||
|
|
@ -7,40 +7,54 @@ function Get-IcingaCommandDependency()
|
|||
[string]$CmdType = ''
|
||||
);
|
||||
|
||||
# Function, Cmdlet, Alias, Modules, Application
|
||||
if ([string]::IsNullOrEmpty($CmdType)) {
|
||||
return $CompiledList;
|
||||
}
|
||||
|
||||
# Create the list container for our object type if not existing
|
||||
# => Function, Cmdlet, Alias, Modules, Application
|
||||
if ($CompiledList.ContainsKey($CmdType) -eq $FALSE) {
|
||||
$CompiledList.Add($CmdType, @{ });
|
||||
}
|
||||
|
||||
# e.g. Invoke-IcingaCheckCPU
|
||||
if ($CompiledList[$CmdType].ContainsKey($CmdName)) {
|
||||
$CompiledList[$CmdType][$CmdName] += 1;
|
||||
|
||||
return $CompiledList;
|
||||
}
|
||||
|
||||
# Add the command this function is called with
|
||||
$CompiledList[$CmdType].Add($CmdName, 0);
|
||||
|
||||
# The command is not known in our Framework dependency list -> could be a native Windows command
|
||||
if ((Test-PSCustomObjectMember -PSObject $DependencyList -Name $CmdName) -eq $FALSE) {
|
||||
return $CompiledList;
|
||||
}
|
||||
|
||||
# Loop our entire dependency list for every single command
|
||||
foreach ($CmdList in $DependencyList.$CmdName.PSObject.Properties.Name) {
|
||||
# $Cmd => The list of child commands
|
||||
# $CmdList => Function, Cmdlet, Alias, Modules, Application
|
||||
$Cmd = $DependencyList.$CmdName.$CmdList;
|
||||
|
||||
# Create the list container for our object type if not existing
|
||||
# => Function, Cmdlet, Alias, Modules, Application
|
||||
if ($CompiledList.ContainsKey($CmdList) -eq $FALSE) {
|
||||
$CompiledList.Add($CmdList, @{ });
|
||||
}
|
||||
|
||||
# Loop all commands within our child list for this command
|
||||
foreach ($entry in $Cmd.PSObject.Properties.Name) {
|
||||
if ($CompiledList[$CmdList].ContainsKey($entry) -eq $FALSE) {
|
||||
$CompiledList[$CmdList].Add($entry, 0);
|
||||
|
||||
# $entry => The command name e.g. Write-IcingaConsolePlain
|
||||
if ($CompiledList[$CmdList].ContainsKey($entry) -eq $FALSE) {
|
||||
$CompiledList = Get-IcingaCommandDependency `
|
||||
-DependencyList $DependencyList `
|
||||
-CompiledList $CompiledList `
|
||||
-CmdName $entry;
|
||||
-CmdName $entry `
|
||||
-CmdType $CmdList;
|
||||
} else {
|
||||
$CompiledList[$CmdList][$entry] += 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -147,11 +147,17 @@ function Get-IcingaJEAConfiguration()
|
|||
-CmdType 'Function';
|
||||
|
||||
# We need to add this function for our background daemon we start with 'Start-IcingaForWindowsDaemon',
|
||||
# as this function is called outside the JEA context
|
||||
# as these functions are called outside the JEA context
|
||||
$UsedCmdlets = Get-IcingaCommandDependency `
|
||||
-DependencyList $DependencyList `
|
||||
-CompiledList $UsedCmdlets `
|
||||
-CmdName 'Add-IcingaForWindowsDaemon' `
|
||||
-CmdName 'Start-IcingaPowerShellDaemon' `
|
||||
-CmdType 'Function';
|
||||
|
||||
$UsedCmdlets = Get-IcingaCommandDependency `
|
||||
-DependencyList $DependencyList `
|
||||
-CompiledList $UsedCmdlets `
|
||||
-CmdName 'Start-IcingaForWindowsDaemon' `
|
||||
-CmdType 'Function';
|
||||
|
||||
# Fixes error if only the Icinga PowerShell Framework is installed, which then causes JEA to fail entirely because of this missing Cmdlet
|
||||
|
|
|
|||
|
|
@ -22,6 +22,10 @@ function Read-IcingaPowerShellModuleFile()
|
|||
[hashtable]$CmdCache = @{ };
|
||||
[hashtable]$FncCache = @{ };
|
||||
[int]$Index = 0;
|
||||
[bool]$ThreadCommand = $FALSE;
|
||||
[bool]$ThreadFetchNext = $FALSE;
|
||||
[bool]$ShellCommand = $FALSE;
|
||||
[bool]$ShellGroupStart = $FALSE;
|
||||
|
||||
foreach ($entry in $PSParser) {
|
||||
if ($entry.Type -eq 'Comment') {
|
||||
|
|
@ -31,6 +35,12 @@ function Read-IcingaPowerShellModuleFile()
|
|||
$CommandList += [string]$entry.Content;
|
||||
$CmdCache.Add($entry.Content, 0);
|
||||
}
|
||||
|
||||
# We need to include commands we call with New-IcingaThreadInstance e.g.
|
||||
# => New-IcingaThreadInstance -Name "Main" -ThreadPool (Get-IcingaThreadPool -Name 'MainPool') -Command 'Add-IcingaForWindowsDaemon' -Start;
|
||||
if ($entry.Content.ToLower() -eq 'new-icingathreadinstance') {
|
||||
$ThreadCommand = $TRUE;
|
||||
}
|
||||
} elseif ($entry.Type -eq 'CommandArgument') {
|
||||
if ($PSParser[$index - 1].Type -eq 'Keyword' -And $PSParser[$index - 1].Content.ToLower() -eq 'function') {
|
||||
if ($FncCache.ContainsKey($entry.Content) -eq $FALSE) {
|
||||
|
|
@ -38,6 +48,55 @@ function Read-IcingaPowerShellModuleFile()
|
|||
$FncCache.Add($entry.Content, 0);
|
||||
}
|
||||
}
|
||||
} elseif ($entry.Type -eq 'Member' -And $entry.Content.ToLower() -eq 'addcommand') {
|
||||
# In case we have objects that use .AddCommand() we should add these to our function list e.g.
|
||||
# => [void]$Shell.AddCommand('Set-IcingaEnvironmentGlobal');
|
||||
$ShellCommand = $TRUE;
|
||||
}
|
||||
|
||||
# If we reached -Command for New-IcingaThreadInstance, check for the String element and add its value to our function list e.g.
|
||||
# => Add-IcingaForWindowsDaemon
|
||||
if ($ThreadFetchNext) {
|
||||
if ($entry.Type -eq 'String') {
|
||||
if (Test-IcingaFunction $entry.Content) {
|
||||
if ($FncCache.ContainsKey($entry.Content) -eq $FALSE) {
|
||||
$FunctionList += [string]$entry.Content;
|
||||
$FncCache.Add($entry.Content, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
$ThreadFetchNext = $FALSE;
|
||||
}
|
||||
|
||||
# If we found the command New-IcingaThreadInstance inside ths script, loop until we reach -Command
|
||||
if ($ThreadCommand) {
|
||||
if ($entry.Type -eq 'CommandParameter' -And $entry.Content.ToLower() -eq '-command') {
|
||||
$ThreadFetchNext = $TRUE;
|
||||
$ThreadCommand = $FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
# If we reached the string content of our .AddCommand() object. add its value to our function list e.g.
|
||||
# => Set-IcingaEnvironmentGlobal
|
||||
if ($ShellGroupStart) {
|
||||
if ($entry.Type -eq 'String') {
|
||||
if (Test-IcingaFunction $entry.Content) {
|
||||
if ($FncCache.ContainsKey($entry.Content) -eq $FALSE) {
|
||||
$FunctionList += [string]$entry.Content;
|
||||
$FncCache.Add($entry.Content, 0);
|
||||
}
|
||||
}
|
||||
|
||||
$ShellGroupStart = $FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
# If we found an .AddArgument() member, continue until our group starts with (
|
||||
if ($ShellCommand) {
|
||||
if ($entry.Type -eq 'GroupStart' -And $entry.Content.ToLower() -eq '(') {
|
||||
$ShellCommand = $FALSE;
|
||||
$ShellGroupStart = $TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
$Index += 1;
|
||||
|
|
|
|||
24
lib/core/jea/Set-IcingaForWindowsServiceJEAProfile.psm1
Normal file
24
lib/core/jea/Set-IcingaForWindowsServiceJEAProfile.psm1
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
function Set-IcingaForWindowsServiceJEAProfile()
|
||||
{
|
||||
[string]$JeaProfile = Get-IcingaJEAContext;
|
||||
$IcingaForWindowsService = Get-IcingaForWindowsServiceData;
|
||||
|
||||
if ([string]::IsNullOrEmpty($IcingaForWindowsService.FullPath) -Or (Test-Path $IcingaForWindowsService.FullPath) -eq $FALSE) {
|
||||
return;
|
||||
}
|
||||
|
||||
[string]$PreparedServicePath = [string]::Format(
|
||||
'\"{0}\" \"{1}\" \"{2}\"',
|
||||
$IcingaForWindowsService.FullPath,
|
||||
(Get-IcingaPowerShellModuleFile),
|
||||
$JeaProfile
|
||||
);
|
||||
|
||||
$Result = Start-IcingaProcess -Executable 'sc.exe' -Arguments ([string]::Format('config icingapowershell binPath= "{0}"', $PreparedServicePath));
|
||||
|
||||
if ($Result.ExitCode -ne 0) {
|
||||
Write-IcingaConsoleError 'Failed to update Icinga for Windows service for JEA profile "{0}": {1}{2}' -Objects $JeaProfile, $ResolveStatus.Message, $ResolveStatus.Error;
|
||||
} else {
|
||||
Write-IcingaConsoleNotice 'Icinga for Windows service JEA handling has been configured successfully to profile "{0}"' -Objects $JeaProfile;
|
||||
}
|
||||
}
|
||||
|
|
@ -45,6 +45,12 @@ function New-IcingaThreadInstance()
|
|||
[void]$Shell.AddParameter('GlobalEnvironment', $Global:Icinga.Public);
|
||||
}
|
||||
|
||||
# Set the JEA context for all threads
|
||||
if ($null -ne $Global:Icinga -And $Global:Icinga.ContainsKey('Protected') -And $Global:Icinga.Protected.ContainsKey('JEAContext')) {
|
||||
[void]$Shell.AddCommand('Set-IcingaEnvironmentJEA');
|
||||
[void]$Shell.AddParameter('JeaEnabled', $Global:Icinga.Protected.JEAContext);
|
||||
}
|
||||
|
||||
[void]$Shell.AddCommand($Command);
|
||||
|
||||
$CodeHash = $Command;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ function New-IcingaThreadPool()
|
|||
$SessionFile = Get-IcingaJEASessionFile;
|
||||
|
||||
if ([string]::IsNullOrEmpty((Get-IcingaJEAContext))) {
|
||||
$SessionConfiguration = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault();
|
||||
$SessionConfiguration = [System.Management.Automation.RunSpaces.InitialSessionState]::CreateDefault();
|
||||
} else {
|
||||
if ([string]::IsNullOrEmpty($SessionFile)) {
|
||||
Write-IcingaEventMessage -EventId 1502 -Namespace 'Framework';
|
||||
|
|
@ -18,14 +18,14 @@ function New-IcingaThreadPool()
|
|||
$SessionConfiguration = [System.Management.Automation.Runspaces.InitialSessionState]::CreateFromSessionConfigurationFile($SessionFile);
|
||||
}
|
||||
|
||||
$Runspaces = [RunspaceFactory]::CreateRunspacePool(
|
||||
$RunSpaces = [RunSpaceFactory]::CreateRunSpacePool(
|
||||
$MinInstances,
|
||||
$MaxInstances,
|
||||
$SessionConfiguration,
|
||||
$host
|
||||
)
|
||||
|
||||
$Runspaces.Open();
|
||||
$RunSpaces.Open();
|
||||
|
||||
return $Runspaces;
|
||||
return $RunSpaces;
|
||||
}
|
||||
|
|
|
|||
10
lib/core/thread/Set-IcingaEnvironmentJEA.psm1
Normal file
10
lib/core/thread/Set-IcingaEnvironmentJEA.psm1
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
function Set-IcingaEnvironmentJEA()
|
||||
{
|
||||
param (
|
||||
[bool]$JeaEnabled = $FALSE
|
||||
);
|
||||
|
||||
if ($null -ne $Global:Icinga -And $Global:Icinga.ContainsKey('Protected') -And $Global:Icinga.Protected.ContainsKey('JEAContext')) {
|
||||
$Global:Icinga.Protected.JEAContext = $JeaEnabled;
|
||||
}
|
||||
}
|
||||
|
|
@ -2,20 +2,23 @@ function Start-IcingaPowerShellDaemon()
|
|||
{
|
||||
param (
|
||||
[switch]$RunAsService = $FALSE,
|
||||
[switch]$JEAContext = $FALSE,
|
||||
[switch]$JEARestart = $FALSE
|
||||
);
|
||||
|
||||
Start-IcingaForWindowsDaemon -RunAsService:$RunAsService -JEARestart:$JEARestart;
|
||||
Start-IcingaForWindowsDaemon -RunAsService:$RunAsService -JEARestart:$JEARestart -JEAContext:$JEAContext;
|
||||
}
|
||||
|
||||
function Start-IcingaForWindowsDaemon()
|
||||
{
|
||||
param (
|
||||
[switch]$RunAsService = $FALSE,
|
||||
[switch]$JEAContext = $FALSE,
|
||||
[switch]$JEARestart = $FALSE
|
||||
);
|
||||
|
||||
$Global:Icinga.Protected.RunAsDaemon = $TRUE;
|
||||
$Global:Icinga.Protected.RunAsDaemon = [bool]$RunAsService;
|
||||
$Global:Icinga.Protected.JEAContext = [bool]$JEAContext;
|
||||
[string]$MainServicePidFile = (Join-Path -Path (Get-IcingaCacheDir) -ChildPath 'service.pid');
|
||||
[string]$JeaPidFile = (Join-Path -Path (Get-IcingaCacheDir) -ChildPath 'jea.pid');
|
||||
[string]$JeaProfile = Get-IcingaPowerShellConfig -Path 'Framework.JEAProfile';
|
||||
|
|
@ -32,7 +35,6 @@ function Start-IcingaForWindowsDaemon()
|
|||
if ([string]::IsNullOrEmpty($JeaProfile)) {
|
||||
Write-IcingaDebugMessage -Message 'Starting Icinga for Windows service without JEA context' -Objects $RunAsService, $JEARestart, $JeaProfile;
|
||||
|
||||
$Global:Icinga.Protected.RunAsDaemon = $TRUE;
|
||||
# Todo: Add config for active background tasks. Set it to 20 for the moment
|
||||
Add-IcingaThreadPool -Name 'MainPool' -MaxInstances 20;
|
||||
$Global:Icinga.Public.Add('SSLCertificate', $Certificate);
|
||||
|
|
@ -41,6 +43,7 @@ function Start-IcingaForWindowsDaemon()
|
|||
New-IcingaThreadInstance -Name "Main" -ThreadPool (Get-IcingaThreadPool -Name 'MainPool') -Command 'Add-IcingaForWindowsDaemon' -Start;
|
||||
} else {
|
||||
Write-IcingaDebugMessage -Message 'Starting Icinga for Windows service inside JEA context' -Objects $RunAsService, $JEARestart, $JeaProfile;
|
||||
|
||||
& powershell.exe -NoProfile -NoLogo -ConfigurationName $JeaProfile -Command {
|
||||
try {
|
||||
Use-Icinga -Daemon;
|
||||
|
|
@ -84,7 +87,7 @@ function Start-IcingaForWindowsDaemon()
|
|||
|
||||
Write-IcingaFileSecure -File $JeaPidFile -Value '';
|
||||
Write-IcingaEventMessage -EventId 1505 -Namespace Framework -Objects ([string]::Format('{0}/5', $JeaRestartCounter));
|
||||
Start-IcingaForWindowsDaemon -RunAsService:$RunAsService -JEARestart;
|
||||
Start-IcingaForWindowsDaemon -RunAsService:$RunAsService -JEAContext:$JEAContext -JEARestart;
|
||||
|
||||
$JeaRestartCounter += 1;
|
||||
$JeaPid = '';
|
||||
|
|
|
|||
|
|
@ -9,10 +9,6 @@ function New-IcingaForWindowsRESTApi()
|
|||
$RequireAuth
|
||||
);
|
||||
|
||||
# Import the framework library components and initialise it
|
||||
# as daemon
|
||||
Use-Icinga -LibOnly -Daemon;
|
||||
|
||||
$RESTEndpoints = Invoke-IcingaNamespaceCmdlets -Command 'Register-IcingaRESTAPIEndpoint*';
|
||||
Write-IcingaDebugMessage -Message (
|
||||
[string]::Format(
|
||||
|
|
|
|||
|
|
@ -5,10 +5,6 @@ function New-IcingaForWindowsRESTThread()
|
|||
$ThreadId
|
||||
);
|
||||
|
||||
# Import the framework library components and initialise it
|
||||
# as daemon
|
||||
Use-Icinga -LibOnly -Daemon;
|
||||
|
||||
# Initialise our performance counter categories
|
||||
Show-IcingaPerformanceCounterCategories | Out-Null;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue