203 lines
7.6 KiB
PowerShell
203 lines
7.6 KiB
PowerShell
#Requires -RunAsAdministrator
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
$Target = "C:\MinerOpsAgent"
|
|
$BackupRoot = Join-Path $Target "backups"
|
|
$BackupTimestamp = Get-Date -Format "yyyyMMdd-HHmmss"
|
|
$BackupSuffix = [guid]::NewGuid().ToString("N").Substring(0, 8)
|
|
$Backup = Join-Path $BackupRoot "$BackupTimestamp-$BackupSuffix"
|
|
$RuntimeFiles = @(
|
|
"MinerOpsAgentService.ps1", "MinerOpsAgentTray.ps1", "MinerOpsAgentGUI.ps1",
|
|
"MinerOpsApi3EnrichmentPoller.ps1", "MinerOpsPoolPoller.ps1",
|
|
"MinerOpsPoolPollerTask.ps1", "EnrichOneMiner.ps1", "MinerOpsControl.ps1",
|
|
"VERSION"
|
|
)
|
|
|
|
function Get-MinerOpsScheduledTask {
|
|
param([Parameter(Mandatory)][string]$Name)
|
|
|
|
return Get-ScheduledTask -TaskName $Name -ErrorAction SilentlyContinue
|
|
}
|
|
|
|
function Stop-MinerOpsScheduledTaskForUpgrade {
|
|
param(
|
|
[Parameter(Mandatory)][string]$Name,
|
|
[TimeSpan]$Timeout = [TimeSpan]::FromSeconds(30)
|
|
)
|
|
|
|
Stop-ScheduledTask -TaskName $Name -ErrorAction Stop
|
|
$deadline = [DateTime]::UtcNow.Add($Timeout)
|
|
do {
|
|
Start-Sleep -Milliseconds 500
|
|
$task = Get-MinerOpsScheduledTask -Name $Name
|
|
if ($null -eq $task) {
|
|
throw "Scheduled Task $Name disappeared while stopping it."
|
|
}
|
|
if ([string]$task.State -ne "Running") {
|
|
return
|
|
}
|
|
} while ([DateTime]::UtcNow -lt $deadline)
|
|
|
|
throw "Scheduled Task $Name did not stop within $($Timeout.TotalSeconds) seconds."
|
|
}
|
|
|
|
function Start-MinerOpsScheduledTask {
|
|
param([Parameter(Mandatory)][string]$Name)
|
|
|
|
Start-ScheduledTask -TaskName $Name -ErrorAction Stop
|
|
}
|
|
|
|
# Validate the complete source before creating directories, stopping tasks, or
|
|
# replacing any target file.
|
|
foreach ($name in $RuntimeFiles) {
|
|
$source = Join-Path $PSScriptRoot $name
|
|
if (!(Test-Path -LiteralPath $source -PathType Leaf)) {
|
|
throw "Deployment source is missing $name"
|
|
}
|
|
}
|
|
|
|
$mainTask = Get-MinerOpsScheduledTask -Name "MinerOpsAgent"
|
|
$mainTaskExisted = $null -ne $mainTask
|
|
$mainTaskWasRunning = $mainTaskExisted -and [string]$mainTask.State -eq "Running"
|
|
$mainTaskStoppedForUpgrade = $false
|
|
$deploymentStarted = $false
|
|
$existing = @()
|
|
$createdTaskNames = [System.Collections.Generic.List[string]]::new()
|
|
|
|
try {
|
|
New-Item -ItemType Directory -Force -Path $Target, $BackupRoot | Out-Null
|
|
$existing = @($RuntimeFiles | Where-Object {
|
|
Test-Path -LiteralPath (Join-Path $Target $_) -PathType Leaf
|
|
})
|
|
if ($existing.Count -gt 0) {
|
|
New-Item -ItemType Directory -Force -Path $Backup | Out-Null
|
|
foreach ($name in $existing) {
|
|
Copy-Item -LiteralPath (Join-Path $Target $name) `
|
|
-Destination (Join-Path $Backup $name) -Force
|
|
}
|
|
}
|
|
|
|
if ($mainTaskWasRunning) {
|
|
$mainTaskStoppedForUpgrade = $true
|
|
Stop-MinerOpsScheduledTaskForUpgrade -Name "MinerOpsAgent"
|
|
}
|
|
|
|
$deploymentStarted = $true
|
|
foreach ($name in $RuntimeFiles) {
|
|
Copy-Item -LiteralPath (Join-Path $PSScriptRoot $name) `
|
|
-Destination (Join-Path $Target $name) -Force
|
|
}
|
|
|
|
$powerShell = "$env:SystemRoot\System32\WindowsPowerShell\v1.0\powershell.exe"
|
|
$system = New-ScheduledTaskPrincipal -UserId "SYSTEM" `
|
|
-LogonType ServiceAccount -RunLevel Highest
|
|
$startup = New-ScheduledTaskTrigger -AtStartup
|
|
$fiveMinutes = New-ScheduledTaskTrigger -Once -At (Get-Date).AddMinutes(1) `
|
|
-RepetitionInterval (New-TimeSpan -Minutes 5)
|
|
$commonSettings = New-ScheduledTaskSettingsSet -MultipleInstances IgnoreNew `
|
|
-StartWhenAvailable -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries `
|
|
-ExecutionTimeLimit (New-TimeSpan -Hours 72)
|
|
$agentSettings = New-ScheduledTaskSettingsSet -MultipleInstances IgnoreNew `
|
|
-StartWhenAvailable -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries `
|
|
-ExecutionTimeLimit (New-TimeSpan -Hours 72) -RestartCount 3 `
|
|
-RestartInterval (New-TimeSpan -Minutes 1)
|
|
|
|
$definitions = @(
|
|
@{
|
|
Name = "MinerOpsAgent"; Script = "MinerOpsAgentService.ps1"
|
|
Trigger = $startup; Principal = $system; Settings = $agentSettings
|
|
ExtraArguments = ""
|
|
},
|
|
@{
|
|
Name = "MinerOpsAgentTray"; Script = "MinerOpsAgentTray.ps1"
|
|
Trigger = (New-ScheduledTaskTrigger -AtLogOn); Principal = $null
|
|
Settings = $commonSettings; ExtraArguments = "-WindowStyle Hidden"
|
|
},
|
|
@{
|
|
Name = "MinerOpsApi3Enrichment"
|
|
Script = "MinerOpsApi3EnrichmentPoller.ps1"
|
|
Trigger = $fiveMinutes; Principal = $system; Settings = $commonSettings
|
|
ExtraArguments = "-Limit 0 -TimeoutMs 8000"
|
|
},
|
|
@{
|
|
Name = "MinerOpsPoolPoller"; Script = "MinerOpsPoolPollerTask.ps1"
|
|
Trigger = $fiveMinutes; Principal = $system; Settings = $commonSettings
|
|
ExtraArguments = ""
|
|
}
|
|
)
|
|
foreach ($definition in $definitions) {
|
|
if ($null -ne (Get-MinerOpsScheduledTask -Name $definition.Name)) {
|
|
Write-Host "Preserving existing Scheduled Task $($definition.Name)."
|
|
continue
|
|
}
|
|
|
|
$arguments = '-NoProfile -ExecutionPolicy Bypass -File "{0}"' -f `
|
|
(Join-Path $Target $definition.Script)
|
|
if ($definition.ExtraArguments) {
|
|
$arguments = "$arguments $($definition.ExtraArguments)"
|
|
}
|
|
$action = New-ScheduledTaskAction -Execute $powerShell -Argument $arguments
|
|
$registration = @{
|
|
TaskName = $definition.Name
|
|
Action = $action
|
|
Trigger = $definition.Trigger
|
|
Settings = $definition.Settings
|
|
}
|
|
if ($definition.Principal) {
|
|
$registration.Principal = $definition.Principal
|
|
}
|
|
Register-ScheduledTask @registration | Out-Null
|
|
$createdTaskNames.Add($definition.Name)
|
|
}
|
|
|
|
if ($mainTaskWasRunning) {
|
|
Start-MinerOpsScheduledTask -Name "MinerOpsAgent"
|
|
$mainTaskStoppedForUpgrade = $false
|
|
}
|
|
|
|
Write-Host "MinerOps Windows Agent installed. Existing task definitions, config, state, logs, and backups were preserved."
|
|
} catch {
|
|
$deploymentError = $_
|
|
$rollbackErrors = [System.Collections.Generic.List[string]]::new()
|
|
|
|
foreach ($taskName in $createdTaskNames) {
|
|
try {
|
|
Unregister-ScheduledTask -TaskName $taskName -Confirm:$false `
|
|
-ErrorAction Stop
|
|
} catch {
|
|
$rollbackErrors.Add("could not remove new task ${taskName}: $($_.Exception.Message)")
|
|
}
|
|
}
|
|
|
|
if ($deploymentStarted) {
|
|
foreach ($name in $RuntimeFiles) {
|
|
try {
|
|
$targetPath = Join-Path $Target $name
|
|
if ($existing -contains $name) {
|
|
Copy-Item -LiteralPath (Join-Path $Backup $name) `
|
|
-Destination $targetPath -Force
|
|
} elseif (Test-Path -LiteralPath $targetPath) {
|
|
Remove-Item -LiteralPath $targetPath -Force
|
|
}
|
|
} catch {
|
|
$rollbackErrors.Add("could not restore ${name}: $($_.Exception.Message)")
|
|
}
|
|
}
|
|
}
|
|
|
|
if ($mainTaskWasRunning -and $mainTaskStoppedForUpgrade) {
|
|
try {
|
|
Start-MinerOpsScheduledTask -Name "MinerOpsAgent"
|
|
$mainTaskStoppedForUpgrade = $false
|
|
} catch {
|
|
$rollbackErrors.Add("could not restart restored MinerOpsAgent: $($_.Exception.Message)")
|
|
}
|
|
}
|
|
|
|
if ($rollbackErrors.Count -gt 0) {
|
|
$rollbackSummary = $rollbackErrors -join "; "
|
|
throw "Deployment failed: $($deploymentError.Exception.Message). Rollback errors: $rollbackSummary"
|
|
}
|
|
throw $deploymentError
|
|
}
|