Public/JetRelay.ps1
. "$PSScriptRoot/../Private/CertificateHelper.ps1" . "$PSScriptRoot/../Private/PlatformHelper.ps1" . "$PSScriptRoot/../Private/DockerHelper.ps1" . "$PSScriptRoot/../Private/CaseHelper.ps1" . "$PSScriptRoot/../Private/YamlHelper.ps1" . "$PSScriptRoot/../Private/CmdletService.ps1" function Get-JetImage { param( [string] $Platform ) $JetVersion = '0.12.0' # Update Get-WaykDenImage as well $image = if ($Platform -ne "windows") { "devolutions/devolutions-jet:${JetVersion}-buster" } else { "devolutions/devolutions-jet:${JetVersion}-servercore-ltsc2019" } return $image } class JetConfig { [string] $JetInstance [string[]] $JetListeners [string] $DockerPlatform [string] $DockerIsolation [string] $DockerRestartPolicy [string] $DockerImage } function Set-JetConfig { [CmdletBinding()] param( [string] $ConfigPath, [string] $JetInstance, [string[]] $JetListeners, [ValidateSet("linux","windows")] [string] $DockerPlatform, [ValidateSet("process","hyperv")] [string] $DockerIsolation, [ValidateSet("no","on-failure","always","unless-stopped")] [string] $DockerRestartPolicy, [string] $DockerImage, [string] $Force ) $ConfigPath = Find-JetConfig -ConfigPath:$ConfigPath if (-Not (Test-Path -Path $ConfigPath -PathType 'Container')) { New-Item -Path $ConfigPath -ItemType 'Directory' } $ConfigFile = Join-Path $ConfigPath "jet-relay.yml" if (-Not (Test-Path -Path $ConfigFile -PathType 'Leaf')) { $config = [JetConfig]::new() } else { $config = Get-JetConfig -ConfigPath:$ConfigPath } $properties = [JetConfig].GetProperties() | ForEach-Object { $_.Name } foreach ($param in $PSBoundParameters.GetEnumerator()) { if ($properties -Contains $param.Key) { $config.($param.Key) = $param.Value } } $JetRelayPath = Join-Path $ConfigPath "jet-relay" New-Item -Path $JetRelayPath -ItemType "Directory" -Force | Out-Null # always force overwriting jet-relay.yml when updating the config file ConvertTo-Yaml -Data (ConvertTo-SnakeCaseObject -Object $config) -OutFile $ConfigFile -Force } function Get-JetConfig { [CmdletBinding()] [OutputType('JetConfig')] param( [string] $ConfigPath, [switch] $Expand ) $ConfigPath = Find-JetConfig -ConfigPath:$ConfigPath $ConfigFile = Join-Path $ConfigPath "jet-relay.yml" $ConfigData = Get-Content -Path $ConfigFile -Raw -ErrorAction Stop $yaml = ConvertFrom-Yaml -Yaml $ConfigData -UseMergingParser -AllDocuments -Ordered $config = [JetConfig]::new() [JetConfig].GetProperties() | ForEach-Object { $Name = $_.Name $snake_name = ConvertTo-SnakeCase -Value $Name if ($yaml.Contains($snake_name)) { if ($yaml.$snake_name -is [string]) { if (![string]::IsNullOrEmpty($yaml.$snake_name)) { $config.$Name = ($yaml.$snake_name).Trim() } } else { $config.$Name = $yaml.$snake_name } } } if ($Expand) { Expand-JetConfig $config } return $config } function Expand-JetConfig { param( [JetConfig] $Config ) if (-Not $config.DockerPlatform) { if (Get-IsWindows) { $config.DockerPlatform = "windows" } else { $config.DockerPlatform = "linux" } } if (-Not $config.DockerRestartPolicy) { $config.DockerRestartPolicy = "on-failure" } if (-Not $config.DockerImage) { $config.DockerImage = Get-JetImage -Platform $config.DockerPlatform } if (-Not $config.JetListeners) { $config.JetListeners = @("tcp://0.0.0.0:8080") } } function Find-JetConfig { param( [string] $ConfigPath ) if (-Not $ConfigPath) { $ConfigPath = Get-Location } if ($Env:JET_CONFIG_PATH) { $ConfigPath = $Env:JET_CONFIG_PATH } return $ConfigPath } function Set-JetConfigPath { [CmdletBinding()] param( [Parameter(Mandatory=$true,Position=0)] [string] $ConfigPath ) $Env:JET_CONFIG_PATH = $ConfigPath } function Get-JetRelayPath() { [CmdletBinding()] param( [Parameter(Mandatory,Position=0)] [ValidateSet("ConfigPath","GlobalPath","LocalPath")] [string] $PathType ) $DisplayName = "Jet Relay" $LowerName = "jet-relay" $CompanyName = "Devolutions" $HomePath = Resolve-Path '~' if (Get-IsWindows) { $LocalPath = $Env:AppData + "\${CompanyName}\${DisplayName}"; $GlobalPath = $Env:ProgramData + "\${CompanyName}\${DisplayName}" } elseif ($IsMacOS) { $LocalPath = "$HomePath/Library/Application Support/${DisplayName}" $GlobalPath = "/Library/Application Support/${DisplayName}" } elseif ($IsLinux) { $LocalPath = "$HomePath/.config/${LowerName}" $GlobalPath = "/etc/${LowerName}" } switch ($PathType) { 'LocalPath' { $LocalPath } 'GlobalPath' { $GlobalPath } 'ConfigPath' { $GlobalPath } default { throw("Invalid path type: $PathType") } } } function Import-JetCertificate { [CmdletBinding()] param( [string] $ConfigPath, [string] $CertificateFile, [string] $PrivateKeyFile, [string] $Password ) $ConfigPath = Find-JetConfig -ConfigPath:$ConfigPath $config = Get-JetConfig -ConfigPath:$ConfigPath $result = Get-PemCertificate -CertificateFile:$CertificateFile ` -PrivateKeyFile:$PrivateKeyFile -Password:$Password $CertificateData = $result.Certificate $PrivateKeyData = $result.PrivateKey $JetRelayPath = Join-Path $ConfigPath "jet-relay" New-Item -Path $JetRelayPath -ItemType "Directory" -Force | Out-Null $JetRelayPemFile = Join-Path $JetRelayPath "jet-relay.pem" $JetRelayKeyFile = Join-Path $JetRelayPath "jet-relay.key" Set-Content -Path $JetRelayPemFile -Value $CertificateData -Force Set-Content -Path $JetRelayKeyFile -Value $PrivateKeyData -Force } function Get-JetService { param( [string] $ConfigPath, [JetConfig] $Config ) if ($config.DockerPlatform -eq "linux") { $PathSeparator = "/" $JetRelayDataPath = "/etc/jet-relay" } else { $PathSeparator = "\" $JetRelayDataPath = "c:\jet-relay" } $Service = [DockerService]::new() $Service.ContainerName = 'devolutions-jet' $Service.Image = $config.DockerImage $Service.Platform = $config.DockerPlatform $Service.Isolation = $config.DockerIsolation $Service.RestartPolicy = $config.DockerRestartPolicy $Service.TargetPorts = @(10256) foreach ($JetListener in $config.JetListeners) { $ListenerUrl = ([string[]] $($JetListener -Split ','))[0] $url = [System.Uri]::new($ListenerUrl) $Service.TargetPorts += @($url.Port) } $Service.PublishAll = $true $Service.Environment = [ordered]@{ "JET_INSTANCE" = $config.JetInstance; "JET_UNRESTRICTED" = "true"; "RUST_BACKTRACE" = "1"; "RUST_LOG" = "info"; } $Service.Volumes = @("$ConfigPath/jet-relay:$JetRelayDataPath") $Service.External = $false if (Test-Path "$ConfigPath/jet-relay/jet-relay.pem" -PathType 'Leaf') { $Service.Environment['JET_CERTIFICATE_FILE'] = @($JetRelayDataPath, 'jet-relay.pem') -Join $PathSeparator } if (Test-Path "$ConfigPath/jet-relay/jet-relay.key" -PathType 'Leaf') { $Service.Environment['JET_PRIVATE_KEY_FILE'] = @($JetRelayDataPath, 'jet-relay.key') -Join $PathSeparator } $args = @() foreach ($JetListener in $config.JetListeners) { $args += @('-l', "`"$JetListener`"") } $Service.Command = $($args -Join " ") return $Service } function Update-JetRelay { [CmdletBinding()] param( [string] $ConfigPath ) $ConfigPath = Find-JetConfig -ConfigPath:$ConfigPath $config = Get-JetConfig -ConfigPath:$ConfigPath Expand-JetConfig -Config $config $Service = Get-JetService -ConfigPath:$ConfigPath -Config:$config Request-ContainerImage -Name $Service.Image } function Start-JetRelay { [CmdletBinding()] param( [string] $ConfigPath, [switch] $SkipPull ) $ConfigPath = Find-JetConfig -ConfigPath:$ConfigPath $config = Get-JetConfig -ConfigPath:$ConfigPath Expand-JetConfig -Config $config $Service = Get-JetService -ConfigPath:$ConfigPath -Config:$config if (-Not $SkipPull) { # pull docker images only if they are not cached locally if (-Not (Get-ContainerImageId -Name $Service.Image)) { Request-ContainerImage -Name $Service.Image } } Start-DockerService -Service $Service -Remove } function Stop-JetRelay { [CmdletBinding()] param( [string] $ConfigPath, [switch] $Remove ) $ConfigPath = Find-JetConfig -ConfigPath:$ConfigPath $config = Get-JetConfig -ConfigPath:$ConfigPath Expand-JetConfig -Config $config $Service = Get-JetService -ConfigPath:$ConfigPath -Config:$config Write-Host "Stopping $($Service.ContainerName)" Stop-Container -Name $Service.ContainerName -Quiet if ($Remove) { Remove-Container -Name $Service.ContainerName } } function Restart-JetRelay { [CmdletBinding()] param( [string] $ConfigPath ) $ConfigPath = Find-JetConfig -ConfigPath:$ConfigPath Stop-JetRelay -ConfigPath:$ConfigPath Start-JetRelay -ConfigPath:$ConfigPath } function Get-JetRelayServiceDefinition() { $ServiceName = "JetRelay" $ModuleName = "WaykDen" $DisplayName = "Jet Relay" $CompanyName = "Devolutions" $Description = "Jet relay service" return [PSCustomObject]@{ ServiceName = $ServiceName DisplayName = $DisplayName Description = $Description CompanyName = $CompanyName ModuleName = $ModuleName StartCommand = "Start-JetRelay" StopCommand = "Stop-JetRelay" WorkingDir = "%ProgramData%\${CompanyName}\${DisplayName}" } } function Register-JetRelayService { [CmdletBinding()] param( [string] $ServicePath, [switch] $Force ) $Definition = Get-JetRelayServiceDefinition if ($ServicePath) { $Definition.WorkingDir = $ServicePath } Register-CmdletService -Definition $Definition -Force:$Force $ServiceName = $Definition.ServiceName $ServicePath = [System.Environment]::ExpandEnvironmentVariables($Definition.WorkingDir) Write-Host "`"$ServiceName`" service has been installed to `"$ServicePath`"" } function Unregister-JetRelayService { [CmdletBinding()] param( [string] $ServicePath, [switch] $Force ) $Definition = Get-JetRelayServiceDefinition if ($ServicePath) { $Definition.WorkingDir = $ServicePath } Unregister-CmdletService -Definition $Definition -Force:$Force } Export-ModuleMember -Function ` Set-JetConfig, Get-JetConfig, ` Set-JetConfigPath, Get-JetRelayPath, ` Import-JetCertificate, ` Start-JetRelay, Stop-JetRelay, Restart-JetRelay, Update-JetRelay, ` Register-JetRelayService, Unregister-JetRelayService |