lib/core/repository/Install-IcingaComponent.psm1
function Install-IcingaComponent() { param ( [string]$Name = $null, [string]$Version = $null, [switch]$Release = $FALSE, [switch]$Snapshot = $FALSE, [switch]$Confirm = $FALSE, [switch]$Force = $FALSE ); if ([string]::IsNullOrEmpty($Name)) { Write-IcingaConsoleError 'You have to provide a component name'; return; } # Branch snapshot versions will have '/' inside their name if ($Name.Contains('/') -And $Snapshot) { $Name = $Name.Split('/')[0]; } Set-IcingaTLSVersion; if ($Version -eq 'release') { $Version = $null; } if ($Release -eq $TRUE -And $Snapshot -eq $TRUE) { Write-IcingaConsoleError 'You can only select either "Release" or "Snapshot" channel for package installation'; return; } if ($Release -eq $FALSE -And $Snapshot -eq $FALSE) { $Release = $TRUE; } $LockedVersion = Get-IcingaComponentLock -Name $Name; if ($null -ne $LockedVersion) { $Version = $LockedVersion; Write-IcingaConsoleNotice 'Component "{0}" is locked to version "{1}"' -Objects $Name, $LockedVersion; } $PackageContent = Get-IcingaRepositoryPackage -Name $Name -Version $Version -Release:$Release -Snapshot:$Snapshot; $InstallPackage = $PackageContent.Package; $SourceRepo = $PackageContent.Source; $RepoName = $PackageContent.Repository; if ($PackageContent.HasPackage -eq $FALSE) { $SearchVersion = 'release'; if ([string]::IsNullOrEmpty($Version) -eq $FALSE) { $SearchVersion = $Version; } if ($Release) { Write-IcingaConsoleError 'The component "{0}" was not found on stable channel with version "{1}"' -Objects $Name, $SearchVersion; return; } if ($Snapshot) { Write-IcingaConsoleError 'The component "{0}" was not found on snapshot channel with version "{1}"' -Objects $Name, $SearchVersion; return; } return; } $FileSource = $InstallPackage.Location; if ($InstallPackage.RelativePath -eq $TRUE) { $FileSource = Join-WebPath -Path ($SourceRepo.Info.RemoteSource.Replace('\', '/')) -ChildPath ($InstallPackage.Location.Replace('\', '/')); } if ($Confirm -eq $FALSE) { if ((Get-IcingaAgentInstallerAnswerInput -Prompt ([string]::Format('Do you want to install component "{0}" from source "{1}" ({2})?', $Name.ToLower(), $RepoName, $FileSource)) -Default 'y').result -ne 1) { return; } } $FileName = $FileSource.SubString($FileSource.LastIndexOf('/') + 1, $FileSource.Length - $FileSource.LastIndexOf('/') - 1); $DownloadDirectory = New-IcingaTemporaryDirectory; $DownloadDestination = (Join-Path -Path $DownloadDirectory -ChildPath $FileName); Write-IcingaConsoleNotice ([string]::Format('Downloading "{0}" from "{1}"', $Name.ToLower(), $FileSource)); if ((Invoke-IcingaWebRequest -UseBasicParsing -Uri $FileSource -OutFile $DownloadDestination).HasErrors) { Write-IcingaConsoleError ([string]::Format('Failed to download "{0}" from "{1}" into "{2}". Starting cleanup process', $Name.ToLower(), $FileSource, $DownloadDestination)); Start-Sleep -Seconds 2; Remove-Item -Path $DownloadDirectory -Recurse -Force; return; } $FileHash = (Get-FileHash -Path $DownloadDestination -Algorithm SHA256).Hash; if ([string]::IsNullOrEmpty($InstallPackage.Hash) -eq $FALSE -And (Get-FileHash -Path $DownloadDestination -Algorithm SHA256).Hash -ne $InstallPackage.Hash) { Write-IcingaConsoleError ([string]::Format('File validation failed. The stored hash inside the repository "{0}" is not matching the file hash "{1}"', $InstallPackage.Hash, $FileHash)); return; } if ([IO.Path]::GetExtension($FileName) -eq '.zip') { <# Handles installation of Icinga for Windows packages and Icinga for Windows service #> Expand-IcingaZipArchive -Path $DownloadDestination -Destination $DownloadDirectory | Out-Null; Start-Sleep -Seconds 2; Remove-Item -Path $DownloadDestination -Force; $FolderContent = Get-ChildItem -Path $DownloadDirectory -Recurse -Include '*.psd1'; <# Handles installation of Icinga for Windows packages #> if ($null -ne $FolderContent -And $FolderContent.Count -ne 0) { $ManifestFile = $null; $PackageName = $null; $PackageRoot = $null; foreach ($manifest in $FolderContent) { $ManifestFile = Read-IcingaPackageManifest -File $manifest.FullName; if ($null -ne $ManifestFile) { $PackageName = $manifest.Name.Replace('.psd1', ''); $PackageRoot = $manifest.FullName.SubString(0, $manifest.FullName.LastIndexOf('\')); $PackageRoot = Join-Path -Path $PackageRoot -ChildPath '\*' break; } } if ($null -eq $ManifestFile) { Write-IcingaConsoleError ([string]::Format('Unable to read manifest for package "{0}". Aborting installation', $Name.ToLower())); Start-Sleep -Seconds 2; Remove-Item -Path $DownloadDirectory -Recurse -Force; return; } $ComponentFolder = Join-Path -Path (Get-IcingaForWindowsRootPath) -ChildPath $PackageName; $ModuleData = (Get-Module -ListAvailable -Name $PackageName -ErrorAction SilentlyContinue) | Sort-Object Version -Descending | Select-Object Version -First 1; [string]$InstallVersion = $null; $ServiceStatus = $null; $AgentStatus = $null; if ($null -ne $ModuleData) { [string]$InstallVersion = $ModuleData.Version; } if ($ManifestFile.ModuleVersion -eq $InstallVersion -And $Force -eq $FALSE) { Write-IcingaConsoleWarning ([string]::Format('The package "{0}" with version "{1}" is already installed. Use "-Force" to re-install the component', $Name.ToLower(), $ManifestFile.ModuleVersion)); Start-Sleep -Seconds 2; Remove-Item -Path $DownloadDirectory -Recurse -Force; return; } # These update steps only apply for the framework if ($Name.ToLower() -eq 'framework') { Remove-IcingaFrameworkDependencyFile; $ServiceStatus = (Get-Service 'icingapowershell' -ErrorAction SilentlyContinue).Status; $AgentStatus = (Get-Service 'icinga2' -ErrorAction SilentlyContinue).Status; if ($ServiceStatus -eq 'Running') { Write-IcingaConsoleNotice 'Stopping Icinga for Windows service'; Stop-IcingaWindowsService; Start-Sleep -Seconds 1; } if ($AgentStatus -eq 'Running') { Write-IcingaConsoleNotice 'Stopping Icinga Agent service'; Stop-IcingaService 'icinga2'; Start-Sleep -Seconds 1; } } if ((Test-Path $ComponentFolder) -eq $FALSE) { [void](New-Item -ItemType Directory -Path $ComponentFolder -Force); } $ComponentFileContent = Get-ChildItem -Path $ComponentFolder; foreach ($entry in $ComponentFileContent) { if (($entry.Name -eq 'cache' -Or $entry.Name -eq 'config' -Or $entry.Name -eq 'certificate') -And $Name.ToLower() -eq 'framework') { continue; } [void](Remove-ItemSecure -Path $entry.FullName -Recurse -Force); } Copy-ItemSecure -Path $PackageRoot -Destination $ComponentFolder -Recurse -Force | Out-Null; Write-IcingaConsoleNotice 'Installing version "{0}" of component "{1}"' -Objects $ManifestFile.ModuleVersion, $Name.ToLower(); Unblock-IcingaPowerShellFiles -Path $ComponentFolder; if ($Name.ToLower() -eq 'framework') { if (Test-IcingaFunction 'Write-IcingaFrameworkCodeCache') { Write-IcingaFrameworkCodeCache; } Import-Module -Name $ComponentFolder -Force; Import-Module -Name $ComponentFolder -Force -Global; # Apply migration tasks Use-Icinga; if ($ServiceStatus -eq 'Running') { Write-IcingaConsoleNotice 'Starting Icinga for Windows service'; Start-IcingaService 'icingapowershell'; } if ($AgentStatus -eq 'Running') { Write-IcingaConsoleNotice 'Starting Icinga Agent service'; Start-IcingaService 'icinga2'; } } else { try { Import-Module -Name $ComponentFolder -Force -ErrorAction Stop; Import-Module -Name $ComponentFolder -Force -Global -ErrorAction Stop; Write-IcingaConsoleNotice 'Installation of component "{0}" with version "{1}" was successful. Open a new PowerShell to apply the changes' -Objects $Name.ToLower(), $ManifestFile.ModuleVersion; } catch { Write-IcingaConsoleError 'Component "{0}" has been installed with version "{1}", but while importing the component an exception was thrown: {2}' -Objects $Name.ToLower(), $ManifestFile.ModuleVersion, $_.Exception.Message; } } # This will ensure that Framework functions will always win over third party functions, overwriting functionality # of the Framework, which might cause problems during installation otherwise Import-Module (Join-Path -Path (Get-IcingaForWindowsRootPath) -ChildPath 'icinga-powershell-framework') -Force; Import-Module (Join-Path -Path (Get-IcingaForWindowsRootPath) -ChildPath 'icinga-powershell-framework') -Global -Force; } else { <# Handles installation of Icinga for Windows service #> $FolderContent = Get-ChildItem -Path $DownloadDirectory -Recurse -Include 'icinga-service.exe'; if ($Name.ToLower() -eq 'service') { $ConfigDirectory = Get-IcingaPowerShellConfig -Path 'Framework.Icinga.IcingaForWindowsService'; $ConfigUser = Get-IcingaPowerShellConfig -Path 'Framework.Icinga.ServiceUser'; $ServiceData = Get-IcingaForWindowsServiceData; $ServiceDirectory = $ServiceData.Directory; $ServiceUser = $ServiceData.User; [int]$Success = -1; if ([string]::IsNullOrEmpty($ConfigDirectory) -eq $FALSE) { $ServiceDirectory = $ConfigDirectory; } if ([string]::IsNullOrEmpty($ConfigUser) -eq $FALSE) { $ServiceUser = $ConfigUser; } else { Set-IcingaPowerShellConfig -Path 'Framework.Icinga.ServiceUser' -Value $ServiceUser; } foreach ($binary in $FolderContent) { if ((Test-IcingaZipBinaryChecksum -Path $binary.FullName) -eq $FALSE) { Write-IcingaConsoleError 'The checksum for the given service binary does not match'; continue; } if ((Test-Path $ServiceDirectory) -eq $FALSE) { [void](New-Item -ItemType Directory -Path $ServiceDirectory -Force); } $UpdateBin = Join-Path -Path $ServiceDirectory -ChildPath 'icinga-service.exe.update'; $ServiceBin = Join-Path -Path $ServiceDirectory -ChildPath 'icinga-service.exe'; # Service is already installed if (Test-Path $ServiceBin) { $InstalledService = Read-IcingaServicePackage -File $ServiceBin; $NewService = Read-IcingaServicePackage -File $binary.FullName; if ($InstalledService.ProductVersion -eq $NewService.ProductVersion -And $null -ne $InstalledService -And $null -ne $NewService -And $Force -eq $FALSE) { $Success = 0; break; } } Write-IcingaConsoleNotice 'Installing component "service" into "{0}"' -Objects $ServiceDirectory; Copy-ItemSecure -Path $binary.FullName -Destination $UpdateBin -Force | Out-Null; [void](Install-IcingaForWindowsService -Path $ServiceBin -User $ServiceUser -Password (Get-IcingaInternalPowerShellServicePassword)); Update-IcingaServiceUser; Set-IcingaInternalPowerShellServicePassword -Password $null; $Success = 1; break; } if ($Success -eq 0) { Write-IcingaConsoleWarning ([string]::Format('The package "service" with version "{0}" is already installed. Use "-Force" to re-install the component', $InstalledService.ProductVersion)); Remove-Item -Path $DownloadDirectory -Recurse -Force; return; } if ($Success -eq 1) { Remove-Item -Path $DownloadDirectory -Recurse -Force; Write-IcingaConsoleNotice 'Installation of component "service" was successful'; return; } Write-IcingaConsoleError 'Failed to install component "service". Either the package did not include a service binary or the checksum of the binary did not match'; Start-Sleep -Seconds 2; Remove-Item -Path $DownloadDirectory -Recurse -Force; return; } else { Write-IcingaConsoleError 'There was no manifest file found inside the package'; Remove-ItemSecure -Path $DownloadDirectory -Recurse -Force; return; } } } elseif ([IO.Path]::GetExtension($FileName) -eq '.msi') { <# Handles installation of Icinga Agent MSI Packages #> $IcingaData = Get-IcingaAgentInstallation; $InstalledVersion = Get-IcingaAgentVersion; $InstallTarget = $IcingaData.RootDir; $InstallDir = Get-IcingaPowerShellConfig -Path 'Framework.Icinga.AgentLocation'; $ConfigUser = Get-IcingaPowerShellConfig -Path 'Framework.Icinga.ServiceUser'; $ServiceUser = $IcingaData.User; if ([string]::IsNullOrEmpty($InstallDir) -eq $FALSE) { if ((Test-Path $InstallDir) -eq $FALSE) { [void](New-Item -Path $InstallDir -ItemType Directory -Force); } $InstallTarget = $InstallDir; } if ([string]::IsNullOrEmpty($ConfigUser) -eq $FALSE) { $ServiceUser = $ConfigUser; } else { Set-IcingaPowerShellConfig -Path 'Framework.Icinga.ServiceUser' -Value $ServiceUser; } Set-IcingaPowerShellConfig -Path 'Framework.Icinga.AgentLocation' -Value $InstallTarget; [string]$InstallFolderMsg = $InstallTarget; if ([string]::IsNullOrEmpty($InstallTarget) -eq $FALSE) { $InstallTarget = [string]::Format(' INSTALL_ROOT="{0}"', $InstallTarget); } else { $InstallTarget = ''; if ($IcingaData.Architecture -eq 'x86') { $InstallFolderMsg = Join-Path -Path ${env:ProgramFiles(x86)} -ChildPath 'icinga2'; } else { $InstallFolderMsg = Join-Path -Path $env:ProgramFiles -ChildPath 'icinga2'; } } $MSIData = & powershell.exe -Command { Use-Icinga; return Read-IcingaMSIMetadata -File $args[0] } -Args $DownloadDestination; if ($InstalledVersion.Full -eq $MSIData.ProductVersion -And $Force -eq $FALSE) { Write-IcingaConsoleWarning 'The package "agent" with version "{0}" is already installed. Use "-Force" to re-install the component' -Objects $InstalledVersion.Full; Remove-Item -Path $DownloadDirectory -Recurse -Force; return; } Write-IcingaConsoleNotice 'Installing component "agent" with version "{0}" into "{1}"' -Objects $MSIData.ProductVersion, $InstallFolderMsg; if ($IcingaData.Installed) { if ((Uninstall-IcingaAgent) -eq $FALSE) { return; } } $InstallProcess = powershell.exe -Command { $IcingaInstaller = $args[0]; $InstallTarget = $args[1]; Use-Icinga; $InstallProcess = Start-IcingaProcess -Executable 'MsiExec.exe' -Arguments ([string]::Format('/quiet /i "{0}" {1}', $IcingaInstaller, $InstallTarget)) -FlushNewLines; return $InstallProcess; } -Args $DownloadDestination, $InstallTarget; if ($InstallProcess.ExitCode -ne 0) { Write-IcingaConsoleError -Message 'Failed to install component "agent": {0}{1}' -Objects $InstallProcess.Message, $InstallProcess.Error; return $FALSE; } Reset-IcingaAgentConfigFile; Move-IcingaAgentDefaultConfig; Set-IcingaServiceUser -User $ServiceUser -SetPermission | Out-Null; Update-IcingaServiceUser; Write-IcingaConsoleNotice 'Installation of component "agent" with version "{0}" was successful.' -Objects $MSIData.ProductVersion; } else { Write-IcingaConsoleError ([string]::Format('Unsupported file extension "{0}" found for package "{1}". Aborting installation', ([IO.Path]::GetExtension($FileName)), $Name.ToLower())); } Start-Sleep -Seconds 1; Remove-Item -Path $DownloadDirectory -Recurse -Force; } |