217 lines
8.1 KiB
PowerShell
217 lines
8.1 KiB
PowerShell
#Requires -Version 5.1
|
|
Set-StrictMode -Version Latest
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
$serverUrl = if ($env:OTO_BOOTSTRAP_SERVER_URL) { $env:OTO_BOOTSTRAP_SERVER_URL } else { '' }
|
|
$socketUrl = if ($env:OTO_BOOTSTRAP_SOCKET_URL) { $env:OTO_BOOTSTRAP_SOCKET_URL } else { '' }
|
|
$agentId = if ($env:OTO_AGENT_ID) { $env:OTO_AGENT_ID } else { '' }
|
|
$enrollmentToken = ''
|
|
$releaseBaseUrl = if ($env:OTO_BOOTSTRAP_RELEASE_BASE_URL) { $env:OTO_BOOTSTRAP_RELEASE_BASE_URL } else { '' }
|
|
$agentAlias = if ($env:OTO_AGENT_ALIAS) { $env:OTO_AGENT_ALIAS } else { '' }
|
|
$installDir = Join-Path $env:LOCALAPPDATA 'oto\bin'
|
|
$configPath = Join-Path $env:LOCALAPPDATA 'oto\agent\config.yaml'
|
|
$workspaceRoot = Join-Path $env:LOCALAPPDATA 'oto\workspace'
|
|
$logDir = Join-Path $env:LOCALAPPDATA 'oto\agent\log'
|
|
$background = $true
|
|
|
|
function Get-HashHex([string]$value) {
|
|
$sha = [System.Security.Cryptography.SHA256]::Create()
|
|
try {
|
|
$bytes = [System.Text.Encoding]::UTF8.GetBytes($value)
|
|
$hashBytes = $sha.ComputeHash($bytes)
|
|
return -join ($hashBytes | ForEach-Object { $_.ToString('x2') })
|
|
} finally {
|
|
$sha.Dispose()
|
|
}
|
|
}
|
|
|
|
function Get-DeviceIdentitySource {
|
|
$source = ''
|
|
try {
|
|
$machineGuid = (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Cryptography' -Name MachineGuid -ErrorAction Stop).MachineGuid
|
|
if ($machineGuid) { $source = [string]$machineGuid }
|
|
} catch {
|
|
$source = ''
|
|
}
|
|
if (-not $source -and $env:COMPUTERNAME) {
|
|
$source = $env:COMPUTERNAME
|
|
}
|
|
if (-not $source) {
|
|
$source = [System.Net.Dns]::GetHostName()
|
|
}
|
|
return $source
|
|
}
|
|
|
|
function Get-DefaultAgentId {
|
|
$hash = Get-HashHex "oto:windows:$(Get-DeviceIdentitySource)"
|
|
return "oto-$($hash.Substring(0, [Math]::Min(32, $hash.Length)))"
|
|
}
|
|
|
|
function Get-DefaultAgentAlias {
|
|
if ($env:COMPUTERNAME) { return $env:COMPUTERNAME }
|
|
$hostName = [System.Net.Dns]::GetHostName()
|
|
if ($hostName) { return $hostName }
|
|
return 'oto-runner'
|
|
}
|
|
|
|
$i = 0
|
|
while ($i -lt $args.Count) {
|
|
switch ($args[$i]) {
|
|
'--' { $i++ }
|
|
'--server-url' { $serverUrl = $args[$i + 1]; $i += 2 }
|
|
'--socket-url' { $socketUrl = $args[$i + 1]; $i += 2 }
|
|
'--agent-id' { $agentId = $args[$i + 1]; $i += 2 }
|
|
'--token' { $enrollmentToken = $args[$i + 1]; $i += 2 }
|
|
'--release-base-url' { $releaseBaseUrl = $args[$i + 1]; $i += 2 }
|
|
'--agent-alias' { $agentAlias = $args[$i + 1]; $i += 2 }
|
|
'--install-dir' { $installDir = $args[$i + 1]; $i += 2 }
|
|
'--config-path' { $configPath = $args[$i + 1]; $i += 2 }
|
|
'--workspace-root' { $workspaceRoot = $args[$i + 1]; $i += 2 }
|
|
'--log-dir' { $logDir = $args[$i + 1]; $i += 2 }
|
|
'--no-background' { $background = $false; $i++ }
|
|
default { Write-Error "Unknown option: $($args[$i])"; exit 1 }
|
|
}
|
|
}
|
|
|
|
if (-not $serverUrl) { $serverUrl = '__OTO_BOOTSTRAP_DEFAULT_SERVER_URL__' }
|
|
if (-not $socketUrl) { $socketUrl = '__OTO_BOOTSTRAP_DEFAULT_SOCKET_URL__' }
|
|
if (-not $releaseBaseUrl) { $releaseBaseUrl = '__OTO_BOOTSTRAP_DEFAULT_RELEASE_BASE_URL__' }
|
|
if ($serverUrl -eq '__OTO_BOOTSTRAP_DEFAULT_SERVER_URL__') { $serverUrl = '' }
|
|
if ($socketUrl -eq '__OTO_BOOTSTRAP_DEFAULT_SOCKET_URL__') { $socketUrl = '' }
|
|
if ($releaseBaseUrl -eq '__OTO_BOOTSTRAP_DEFAULT_RELEASE_BASE_URL__') { $releaseBaseUrl = '' }
|
|
|
|
if (-not $enrollmentToken) {
|
|
Write-Error 'Error: Missing required argument: --token'
|
|
exit 1
|
|
}
|
|
if (-not $serverUrl) {
|
|
Write-Error 'Error: Missing OTO Core URL. Use --server-url or fetch this script from the OTO Core bootstrap endpoint.'
|
|
exit 1
|
|
}
|
|
if (-not $releaseBaseUrl) {
|
|
Write-Error 'Error: Missing release base URL. Use --release-base-url or fetch this script from the OTO Core bootstrap endpoint.'
|
|
exit 1
|
|
}
|
|
|
|
if (-not $releaseBaseUrl.StartsWith('https://')) {
|
|
Write-Error 'Error: --release-base-url must use https://'
|
|
exit 1
|
|
}
|
|
|
|
if (-not $agentId) { $agentId = Get-DefaultAgentId }
|
|
if (-not $agentAlias) { $agentAlias = Get-DefaultAgentAlias }
|
|
if (-not $agentId) {
|
|
Write-Error 'Error: Unable to derive agent id from this device. Use --agent-id to override.'
|
|
exit 1
|
|
}
|
|
|
|
$arch = $env:PROCESSOR_ARCHITECTURE
|
|
$archName = switch ($arch) {
|
|
'AMD64' { 'x64' }
|
|
'ARM64' { 'arm64' }
|
|
default {
|
|
Write-Error "Error: Unsupported architecture: $arch"
|
|
exit 1
|
|
}
|
|
}
|
|
|
|
$assetName = "oto-windows-$archName.zip"
|
|
$downloadUrl = "$releaseBaseUrl/$assetName"
|
|
|
|
$tmpDir = Join-Path ([System.IO.Path]::GetTempPath()) ([System.IO.Path]::GetRandomFileName())
|
|
New-Item -ItemType Directory -Path $tmpDir | Out-Null
|
|
|
|
try {
|
|
$zipPath = Join-Path $tmpDir $assetName
|
|
|
|
Write-Host 'Downloading OTO agent...'
|
|
Invoke-WebRequest -Uri $downloadUrl -OutFile $zipPath -UseBasicParsing
|
|
|
|
Write-Host 'Extracting OTO agent...'
|
|
Expand-Archive -Path $zipPath -DestinationPath $tmpDir -Force
|
|
|
|
$null = New-Item -ItemType Directory -Path $installDir -Force
|
|
Copy-Item -Path (Join-Path $tmpDir 'oto.exe') -Destination (Join-Path $installDir 'oto.exe') -Force
|
|
|
|
$configDir = Split-Path $configPath -Parent
|
|
$agentStateDir = Join-Path $env:LOCALAPPDATA 'oto\agent'
|
|
$pidPath = Join-Path $agentStateDir 'oto-agent.pid'
|
|
|
|
$null = New-Item -ItemType Directory -Path $configDir -Force
|
|
$null = New-Item -ItemType Directory -Path $agentStateDir -Force
|
|
$null = New-Item -ItemType Directory -Path $workspaceRoot -Force
|
|
$null = New-Item -ItemType Directory -Path $logDir -Force
|
|
|
|
$agentIdYaml = $agentId.Replace("'", "''")
|
|
$agentAliasYaml = $agentAlias.Replace("'", "''")
|
|
$enrollmentTokenYaml = $enrollmentToken.Replace("'", "''")
|
|
$serverUrlYaml = $serverUrl.Replace("'", "''")
|
|
$socketUrlYaml = $socketUrl.Replace("'", "''")
|
|
$installDirYaml = ($installDir -replace '\\', '/').Replace("'", "''")
|
|
$workspaceRootYaml = ($workspaceRoot -replace '\\', '/').Replace("'", "''")
|
|
$logDirYaml = ($logDir -replace '\\', '/').Replace("'", "''")
|
|
$socketUrlLine = ''
|
|
if ($socketUrl) {
|
|
$socketUrlLine = " socket_url: '$socketUrlYaml'`n"
|
|
}
|
|
|
|
$configContent = @"
|
|
agent:
|
|
id: '$agentIdYaml'
|
|
alias: '$agentAliasYaml'
|
|
enrollment_token: '$enrollmentTokenYaml'
|
|
server:
|
|
url: '$serverUrlYaml'
|
|
$socketUrlLine
|
|
runtime:
|
|
install_dir: '$installDirYaml'
|
|
workspace_root: '$workspaceRootYaml'
|
|
log_dir: '$logDirYaml'
|
|
"@
|
|
[System.IO.File]::WriteAllText($configPath, $configContent)
|
|
|
|
$acl = Get-Acl $configPath
|
|
$acl.SetAccessRuleProtection($true, $false)
|
|
$rule = New-Object System.Security.AccessControl.FileSystemAccessRule(
|
|
[System.Security.Principal.WindowsIdentity]::GetCurrent().Name,
|
|
'FullControl',
|
|
'Allow'
|
|
)
|
|
$acl.SetAccessRule($rule)
|
|
Set-Acl -Path $configPath -AclObject $acl
|
|
|
|
$otoBin = Join-Path $installDir 'oto.exe'
|
|
|
|
Write-Host 'Verifying OTO binary...'
|
|
try {
|
|
$versionProc = Start-Process -FilePath $otoBin -ArgumentList '--version' -NoNewWindow -Wait -PassThru
|
|
if ($versionProc.ExitCode -ne 0) {
|
|
Write-Error 'Error: OTO binary verification failed.'
|
|
exit 1
|
|
}
|
|
} catch {
|
|
Write-Error 'Error: OTO binary verification failed.'
|
|
exit 1
|
|
}
|
|
|
|
if ($background) {
|
|
Write-Host 'Starting OTO agent in the background...'
|
|
$outLog = Join-Path $logDir 'oto-agent.out.log'
|
|
$errLog = Join-Path $logDir 'oto-agent.err.log'
|
|
$proc = Start-Process -FilePath $otoBin `
|
|
-ArgumentList "agent run --config `"$configPath`"" `
|
|
-RedirectStandardOutput $outLog `
|
|
-RedirectStandardError $errLog `
|
|
-WindowStyle Hidden `
|
|
-PassThru
|
|
[System.IO.File]::WriteAllText($pidPath, $proc.Id.ToString())
|
|
Write-Host "OTO agent started in the background. PID: $($proc.Id)"
|
|
Write-Host "Log path: $outLog"
|
|
} else {
|
|
Write-Host 'OTO agent is configured to run in the foreground.'
|
|
Write-Host 'To run it manually, use the following command:'
|
|
Write-Host " $otoBin agent run --config `"$configPath`""
|
|
}
|
|
} finally {
|
|
Remove-Item -Recurse -Force -ErrorAction SilentlyContinue $tmpDir
|
|
}
|