Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
256 changes: 147 additions & 109 deletions bootstrap-salt.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@
[CmdletBinding()]
param(
[Parameter(Mandatory=$false, ValueFromPipeline=$True)]
[ValidatePattern('^(\d{4}(\.\d{1,2}){0,2}(\-\d{1})?)|(latest)$', Options=1)]
[Alias("v")]
# The version of the Salt minion to install. Default is "latest" which will
# install the latest version of Salt minion available. Doesn't support
Expand Down Expand Up @@ -153,34 +152,116 @@ function Get-MajorVersion {
return ( $Version -split "\." )[0]
}

function Convert-PSObjectToHashtable {
param (
[Parameter(ValueFromPipeline)]
$InputObject
)
if ($null -eq $InputObject) { return $null }

$is_enum = $InputObject -is [System.Collections.IEnumerable]
$not_string = $InputObject -isnot [string]
if ($is_enum -and $not_string) {
$collection = @(
foreach ($object in $InputObject) {
Convert-PSObjectToHashtable $object
}
)
function Get-AvailableVersions {
# Get available versions from a remote location specified in the Source
# Parameter
Write-Verbose "Getting version information from the repo"
Write-Verbose "base_url: $base_url"

$available_versions = [System.Collections.ArrayList]@()

if ( $base_url.StartsWith("http") -or $base_url.StartsWith("ftp") ) {
# We're dealing with HTTP, HTTPS, or FTP
$response = Invoke-WebRequest "$base_url" -UseBasicParsing
try {
$response = Invoke-WebRequest "$base_url" -UseBasicParsing
} catch {
Write-Host "Failed to get version information" -ForegroundColor Red
exit 1
}

Write-Host -NoEnumerate $collection
} elseif ($InputObject -is [PSObject]) {
$hash = @{}
if ( $response.StatusCode -ne 200 ) {
Write-Host "There was an error getting version information" -ForegroundColor Red
Write-Host "Error: $($response.StatusCode)" -ForegroundColor red
exit 1
}

foreach ($property in $InputObject.PSObject.Properties) {
$hash[$property.Name] = Convert-PSObjectToHashtable $property.Value
$response.links | ForEach-Object {
if ( $_.href.Length -gt 8) {
Write-Host "The content at this location is unexpected" -ForegroundColor Red
Write-Host "Should be a list of directories where the name is a version of Salt" -ForegroundColor Red
exit 1
}
}

$hash
# Getting available versions from response
Write-Verbose "Getting available versions from response"
$filtered = $response.Links | Where-Object -Property href -NE "../"
$filtered | Select-Object -Property href | ForEach-Object {
$available_versions.Add($_.href.Trim("/")) | Out-Null
}
} elseif ( $base_url.StartsWith("\\") -or $base_url -match "^[A-Za-z]:\\" ) {
# We're dealing with a local directory or SMB source
Get-ChildItem -Path $base_url -Directory | ForEach-Object {
$available_versions.Add($_.Name) | Out-Null
}
} else {
$InputObject
Write-Host "Unknown Source Type" -ForegroundColor Red
Write-Host "Must be one of HTTP, HTTPS, FTP, SMB Share, Local Directory" -ForegroundColor Red
exit 1
}

Write-Verbose "Available versions:"
$available_versions | ForEach-Object {
Write-Verbose "- $_"
}

# Get the latest version, should be the last in the list
Write-Verbose "Getting latest available version"
$latest = $available_versions | Select-Object -Last 1
Write-Verbose "Latest available version: $latest"

# Create a versions table
# This will have the latest version available, the latest version available
# for each major version, and every version available. This makes the
# version lookup logic easier. The contents of the versions table can be
# found by running -Verbose
Write-Verbose "Populating the versions table"
$versions_table = [ordered]@{"latest"=$latest}
$available_versions | ForEach-Object {
$versions_table[$(Get-MajorVersion $_)] = $_
$versions_table[$_.ToLower()] = $_.ToLower()
}

Write-Verbose "Versions Table:"
$versions_table | Sort-Object Name | Out-String | ForEach-Object {
Write-Verbose "$_"
}

return $versions_table
}

function Get-HashFromArtifactory {
# This function uses the artifactory API to get the SHA265 Hash for the file
# If Source is NOT artifactory, the sha will not be checked
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[String] $SaltVersion,

[Parameter(Mandatory=$true)]
[String] $SaltFileName
)
if ( $api_url ) {
$full_url = "$api_url/$SaltVersion/$SaltFileName"
Write-Verbose "Querying Artifactory API for hash:"
Write-Verbose $full_url
try {
$response = Invoke-RestMethod $full_url -UseBasicParsing
return $response.checksums.sha256
} catch {
Write-Verbose "Artifactory API Not available or file not"
Write-Verbose "available at specified location"
Write-Verbose "Hash will not be checked"
return ""
}
Write-Verbose "No hash found for this file: $SaltFileName"
Write-Verbose "Hash will not be checked"
return ""
}
Write-Verbose "No artifactory API defined"
Write-Verbose "Hash will not be checked"
return ""
}

function Get-FileHash {
Expand Down Expand Up @@ -313,10 +394,8 @@ if ($majorVersion -lt "3006") {
#===============================================================================
$ConfDir = "$RootDir\conf"
$PkiDir = "$ConfDir\pki\minion"
$RootDir = "$env:ProgramData\Salt Project\Salt"
$DfltUrl = "https://packages.broadcom.com/artifactory/saltproject-generic/windows/"
$ApiUrl = "https://packages.broadcom.com/artifactory/api/storage/saltproject-generic/windows"

$RootDir = "$env:ProgramData\Salt Project\Salt"
# Check for existing installation where RootDir is stored in the registry
$SaltRegKey = "HKLM:\SOFTWARE\Salt Project\Salt"
if (Test-Path -Path $SaltRegKey) {
Expand All @@ -325,6 +404,18 @@ if (Test-Path -Path $SaltRegKey) {
}
}

# Get repo and api URLs. An artifactory URL will have "artifactory" in it
$domain, $target = $RepoUrl -split "/artifactory/"
if ( $target ) {
# Create $base_url and $api_url
$base_url = "$domain/artifactory/$target"
$api_url = "$domain/artifactory/api/storage/$target"
} else {
# This is a non-artifactory url, there is no api
$base_url = $domain
$api_url = ""
}

#===============================================================================
# Verify Parameters
#===============================================================================
Expand All @@ -335,8 +426,8 @@ Write-Verbose "version: $Version"
Write-Verbose "runservice: $RunService"
Write-Verbose "master: $Master"
Write-Verbose "minion: $Minion"
Write-Verbose "repourl: $RepoUrl"
Write-Verbose "apiurl: $ApiUrl"
Write-Verbose "repourl: $base_url"
Write-Verbose "apiurl: $api_url"
Write-Verbose "ConfDir: $ConfDir"
Write-Verbose "RootDir: $RootDir"

Expand Down Expand Up @@ -393,98 +484,42 @@ if ( $ConfigureOnly ) {
#===============================================================================
# Detect architecture
#===============================================================================
if ([IntPtr]::Size -eq 4) {
$arch = "x86"
} else {
$arch = "AMD64"
}
if ([IntPtr]::Size -eq 4) { $arch = "x86" } else { $arch = "AMD64" }

#===============================================================================
# Getting version information from the repo
#===============================================================================
if ( $RepoUrl -eq $DfltUrl ) {
Write-Verbose "Getting version information from Artifactory"
$response = Invoke-WebRequest $ApiUrl -UseBasicParsing
# Convert the output to a powershell object
$psobj = $response.ToString() | ConvertFrom-Json

# Filter the object for folders
$filtered = $psobj.children | Where-Object -Property folder -EQ $true

# Get each uri and add it to the list of versions
$available_versions = [System.Collections.ArrayList]@()
$filtered | Select-Object -Property uri | ForEach-Object {
$available_versions.Add($_.uri.Trim("/")) | Out-Null
}
$versions = Get-AvailableVersions

# Create a versions table, similar to repo.json
# This will have the latest version available, the latest version available for
# each major version, and every version available. This makes the version
# lookup logic easier. You can view the contents of the versions table by
# passing the -Verbose command
$latest = $available_versions | Select-Object -Last 1
$versions_table = [ordered]@{"latest"=$latest}

$available_versions | ForEach-Object {
$versions_table[$(Get-MajorVersion $_)] = $_
$versions_table[$_.ToLower()] = $_.ToLower()
}

Write-Verbose "Available versions:"
$available_versions | ForEach-Object {
Write-Verbose "- $_"
}
Write-Verbose "Versions Table:"
$versions_table | Sort-Object Name | Out-String | Write-Verbose

#===============================================================================
# Validate passed version
#===============================================================================
if ( $versions_table.Contains($Version.ToLower()) ) {
$Version = $versions_table[$Version.ToLower()]
} else {
Write-Host "Version $Version is not available" -ForegroundColor Red
Write-Host "Available versions are:" -ForegroundColor Yellow
$available_versions | ForEach-Object { Write-Host "- $_" -ForegroundColor Yellow }
exit 1
}

#===============================================================================
# Get file url and sha256
#===============================================================================
$saltFileName = "Salt-Minion-$Version-Py3-$arch-Setup.exe"
$response = Invoke-WebRequest "$ApiUrl/$Version/$saltFileName" -UseBasicParsing
$psobj = $response.ToString() | ConvertFrom-Json
$saltFileUrl = $psobj.downloadUri
$saltSha256 = $psobj.checksums.sha256

if ( $saltFileName -and $saltVersion -and $saltSha256) {
Write-Verbose "Found Name, Version, and Sha"
} else {
# We will guess the name of the installer
Write-Verbose "Failed to get Name, Version, and Sha from Artifactory API"
Write-Verbose "We'll try to find the file in standard paths"
$saltFileName = "Salt-Minion-$Version-Py3-$arch-Setup.exe"
$saltVersion = $Version
}
#===============================================================================
# Validate passed version
#===============================================================================
Write-Verbose "Looking up version: $Version"
if ( $versions.Contains($Version.ToLower()) ) {
$Version = $versions[$Version.ToLower()]
Write-Verbose "Found version: $Version"
} else {
# If we're using a custom RepoUrl, we're going to assum that the binary is
# in the reoot of the RepoUrl/Version. We will not check the sha on custom
# repos
$saltFileName = "Salt-Minion-$Version-Py3-$arch-Setup.exe"
$saltFileUrl = "$RepoUrl/$Version/$saltFileName"
$saltVersion = $Version
$saltSha256 = ""
Write-Host "Version $Version is not available" -ForegroundColor Red
Write-Host "Available versions are:" -ForegroundColor Yellow
$versions
exit 1
}

#===============================================================================
# Get file url and sha256
#===============================================================================
$saltFileName = "Salt-Minion-$Version-Py3-$arch-Setup.exe"
$saltFileUrl = "$base_url/$Version/$saltFileName"
$saltSha256 = Get-HashFromArtifactory -SaltVersion $Version -SaltFileName $saltFileName

#===============================================================================
# Download minion setup file
#===============================================================================
Write-Host "===============================================================================" -ForegroundColor Yellow
Write-Host " Bootstrapping Salt Minion" -ForegroundColor Green
Write-Host " - version: $Version"
Write-Host " - file name: $saltFileName"
Write-Host " - file url: $saltFileUrl"
Write-Host " - file url : $saltFileUrl"
Write-Host " - file hash: $saltSha256"
Write-Host " - master: $Master"
Write-Host " - minion id: $Minion"
Expand All @@ -498,16 +533,19 @@ Write-Verbose ""
Write-Verbose "Salt File URL: $saltFileUrl"
Write-Verbose "Local File: $localFile"

if ( Test-Path -Path $localFile ) {Remove-Item -Path $localFile -Force}
Invoke-WebRequest -Uri $saltFileUrl -OutFile $localFile
# Remove existing local file
if ( Test-Path -Path $localFile ) { Remove-Item -Path $localFile -Force }

# Download the file
Invoke-WebRequest -Uri $saltFileUrl -OutFile $localFile
if ( Test-Path -Path $localFile ) {
Write-Host "Success" -ForegroundColor Green
} else {
Write-Host "Failed" -ForegroundColor Red
exit 1
}

# Compare the hash if there is a hash to compare
if ( $saltSha256 ) {
$localSha256 = (Get-FileHash -Path $localFile -Algorithm SHA256).Hash
Write-Host "Comparing Hash: " -NoNewline
Expand Down Expand Up @@ -546,13 +584,13 @@ $process = Start-Process $localFile `
-NoNewWindow -PassThru

# Sometimes the installer hangs... we'll wait 5 minutes and then kill it
Write-Verbose ""
Write-Verbose "Waiting for installer to finish"
$process | Wait-Process -Timeout 300 -ErrorAction SilentlyContinue
$process.Refresh()

if ( !$process.HasExited ) {
Write-Host "Installer Timeout" -ForegroundColor Yellow
Write-Verbose "Installer Timeout"
Write-Host ""
Write-Host "Killing hung installer: " -NoNewline
$process | Stop-Process
$process.Refresh()
Expand Down