diff --git a/eng/common/testproxy/install-test-proxy.ps1 b/eng/common/testproxy/install-test-proxy.ps1 new file mode 100644 index 0000000000..402e5ddc8c --- /dev/null +++ b/eng/common/testproxy/install-test-proxy.ps1 @@ -0,0 +1,33 @@ +<# +.SYNOPSIS +Installs a standalone version of the test-proxy for use in tests. This function is intended to be used in CI/CD pipelines, and leaves behind +the pipeline variable PROXY_EXE which contains the path to the test-proxy executable. + +.PARAMETER Version +The version of the proxy to install. Requires a full version to be provided. EG "1.0.0-dev.20240617.1" + +.PARAMETER Directory +The directory within which the test-proxy exe will exist after this function invokes. Defaults to CWD. +#> +param( + [Parameter(Mandatory = $true)] + $Version, + [Parameter(Mandatory = $true)] + $InstallDirectory +) + +. (Join-Path $PSScriptRoot test-proxy.ps1) + +Write-Host "Attempting to download and install version `"$Version`" into `"$InstallDirectory`"" + +Install-Standalone-TestProxy -Version $Version -Directory $InstallDirectory + +$PROXY_EXE = "" + +if ($IsWindows) { + $PROXY_EXE = Join-Path $InstallDirectory "Azure.Sdk.Tools.TestProxy.exe" +} else { + $PROXY_EXE = Join-Path $InstallDirectory "Azure.Sdk.Tools.TestProxy" +} +Write-Host "Downloaded test-proxy available at $PROXY_EXE." +Write-Host "##vso[task.setvariable variable=PROXY_EXE]$PROXY_EXE" diff --git a/eng/common/testproxy/test-proxy-standalone-tool.yml b/eng/common/testproxy/test-proxy-standalone-tool.yml new file mode 100644 index 0000000000..a836427ad7 --- /dev/null +++ b/eng/common/testproxy/test-proxy-standalone-tool.yml @@ -0,0 +1,83 @@ +# This template sets variable PROXY_PID to be used for shutdown later. +parameters: + rootFolder: '$(Build.SourcesDirectory)' + runProxy: true + targetVersion: '' + templateRoot: '$(Build.SourcesDirectory)' + condition: true + +steps: + - pwsh: | + ${{ parameters.templateRoot }}/eng/common/scripts/trust-proxy-certificate.ps1 + displayName: 'Language Specific Certificate Trust' + condition: and(succeeded(), ${{ parameters.condition }}) + + - task: PowerShell@2 + displayName: 'Override proxy version if necessary' + condition: and(succeeded(), ${{ parameters.condition }}, ne('${{ parameters.targetVersion }}', '')) + inputs: + targetType: filePath + filePath: '${{ parameters.templateRoot }}/eng/common/testproxy/scripts/override-proxy-version.ps1' + arguments: '-TargetVersion "${{ parameters.targetVersion }}"' + pwsh: true + + - pwsh: | + $standardVersion = "${{ parameters.templateRoot }}/eng/common/testproxy/target_version.txt" + $overrideVersion = "${{ parameters.templateRoot }}/eng/target_proxy_version.txt" + + $version = $(Get-Content $standardVersion -Raw).Trim() + + if (Test-Path $overrideVersion) { + $version = $(Get-Content $overrideVersion -Raw).Trim() + } + + Write-Host "Installing test-proxy version $version" + ${{ parameters.templateRoot }}/eng/common/testproxy/install-test-proxy.ps1 -Version $version -InstallDirectory $(Build.BinariesDirectory)/test-proxy + displayName: "Install test-proxy" + condition: and(succeeded(), ${{ parameters.condition }}) + + - pwsh: | + Write-Host "##vso[task.prependpath]$(Build.BinariesDirectory)/test-proxy" + displayName: "Prepend path with test-proxy tool install location" + + - ${{ if eq(parameters.runProxy, 'true') }}: + - pwsh: | + Write-Host "##vso[task.setvariable variable=ASPNETCORE_Kestrel__Certificates__Default__Path]${{ parameters.templateRoot }}/eng/common/testproxy/dotnet-devcert.pfx" + Write-Host "##vso[task.setvariable variable=ASPNETCORE_Kestrel__Certificates__Default__Password]password" + Write-Host "##vso[task.setvariable variable=PROXY_MANUAL_START]true" + displayName: 'Configure Kestrel and PROXY_MANUAL_START Variables' + condition: and(succeeded(), ${{ parameters.condition }}) + + - pwsh: | + $Process = Start-Process $(PROXY_EXE) ` + -ArgumentList "start -u --storage-location ${{ parameters.rootFolder }}" ` + -NoNewWindow -PassThru -RedirectStandardOutput ${{ parameters.rootFolder }}/test-proxy.log + + Write-Host "##vso[task.setvariable variable=PROXY_PID]$($Process.Id)" + displayName: 'Run the testproxy - windows' + condition: and(succeeded(), eq(variables['Agent.OS'],'Windows_NT'), ${{ parameters.condition }}) + + # nohup does NOT continue beyond the current session if you use it within powershell + - bash: | + nohup $(PROXY_EXE) &>${{ parameters.rootFolder }}/test-proxy.log & + + echo $! > $(Build.SourcesDirectory)/test-proxy.pid + echo "##vso[task.setvariable variable=PROXY_PID]$(cat $(Build.SourcesDirectory)/test-proxy.pid)" + displayName: "Run the testproxy - linux/mac" + condition: and(succeeded(), ne(variables['Agent.OS'],'Windows_NT'), ${{ parameters.condition }}) + workingDirectory: "${{ parameters.rootFolder }}" + + - pwsh: | + for ($i = 0; $i -lt 10; $i++) { + try { + Invoke-WebRequest -Uri "http://localhost:5000/Admin/IsAlive" | Out-Null + exit 0 + } catch { + Write-Warning "Failed to successfully connect to test proxy. Retrying..." + Start-Sleep 6 + } + } + Write-Error "Could not connect to test proxy." + exit 1 + displayName: Test Proxy IsAlive + condition: and(succeeded(), ${{ parameters.condition }}) diff --git a/eng/common/testproxy/test-proxy.ps1 b/eng/common/testproxy/test-proxy.ps1 new file mode 100644 index 0000000000..f1bf1eca8f --- /dev/null +++ b/eng/common/testproxy/test-proxy.ps1 @@ -0,0 +1,162 @@ +Set-StrictMode -Version 4 +$AVAILABLE_TEST_PROXY_BINARIES = @{ + "Windows" = @{ + "AMD64" = @{ + "system" = "Windows" + "machine" = "AMD64" + "file_name" = "test-proxy-standalone-win-x64.zip" + "executable" = "Azure.Sdk.Tools.TestProxy.exe" + } + } + "Linux" = @{ + "X86_64" = @{ + "system" = "Linux" + "machine" = "X86_64" + "file_name" = "test-proxy-standalone-linux-x64.tar.gz" + "executable" = "Azure.Sdk.Tools.TestProxy" + } + "ARM64" = @{ + "system" = "Linux" + "machine" = "ARM64" + "file_name" = "test-proxy-standalone-linux-arm64.tar.gz" + "executable" = "Azure.Sdk.Tools.TestProxy" + } + } + "Darwin" = @{ + "X86_64" = @{ + "system" = "Darwin" + "machine" = "X86_64" + "file_name" = "test-proxy-standalone-osx-x64.zip" + "executable" = "Azure.Sdk.Tools.TestProxy" + } + "ARM64" = @{ + "system" = "Darwin" + "machine" = "ARM64" + "file_name" = "test-proxy-standalone-osx-arm64.zip" + "executable" = "Azure.Sdk.Tools.TestProxy" + } + } +} + +function Get-SystemArchitecture { + $unameOutput = uname -m + switch ($unameOutput) { + "x86_64" { return "X86_64" } + "aarch64" { return "ARM64" } + "arm64" { return "ARM64" } + default { throw "Unable to determine system architecture. uname -m returned $unameOutput." } + } +} + +function Get-Proxy-Meta () { + $ErrorActionPreferenceDefault = $ErrorActionPreference + $ErrorActionPreference = "Stop" + + $os = "unknown" + $machine = Get-SystemArchitecture + + if ($IsWindows) { + $os = "Windows" + # we only support x64 on windows, if that doesn't work the platform is unsupported + $machine = "AMD64" + } elseif ($IsLinux) { + $os = "Linux" + } elseif ($IsMacOS) { + $os = "Darwin" + } + + $ErrorActionPreference = $ErrorActionPreferenceDefault + + return $AVAILABLE_TEST_PROXY_BINARIES[$os][$machine] +} + +function Get-Proxy-Url ( + [Parameter(mandatory=$true)]$Version +) { + $systemDetails = Get-Proxy-Meta + + $file = $systemDetails.file_name + $url = "https://github.com/Azure/azure-sdk-tools/releases/download/Azure.Sdk.Tools.TestProxy_$Version/$file" + + return $url +} + +function Cleanup-Directory ($path) { + if (Test-Path -Path $path) { + Remove-Item -Path $path -Recurse -Force + } + New-Item -ItemType Directory -Path $path -Force +} + +function Is-Work-Necessary ( + [Parameter(mandatory=$true)] + $Version, + [Parameter(mandatory=$true)] + $Directory +) { + $savedVersionTxt = Join-Path $Directory "downloaded_version.txt" + if (Test-Path $savedVersionTxt) { + $result = (Get-Content -Raw $savedVersionTxt).Trim() + + if ($result -eq $Version) { + return $false + } + } + + return $true +} + +<# +.SYNOPSIS +Installs a standalone version of the test-proxy. +.PARAMETER Version +The version of the proxy to install. Requires a full version to be provided. EG "1.0.0-dev.20240617.1" +.PARAMETER Directory +The directory within which the test-proxy exe will exist after this function invokes. Defaults to "." +#> +function Install-Standalone-TestProxy ( + [Parameter(mandatory=$true)] + $Version, + $Directory="." +) { + $ErrorActionPreference = "Stop" + $systemDetails = Get-Proxy-Meta + + if (!(Test-Path $Directory) -and $Directory -ne ".") { + New-Item -ItemType Directory -Path $Directory -Force + } + + $downloadFolder = Resolve-Path $Directory + $downloadUrl = Get-Proxy-Url $Version + $downloadFile = $downloadUrl.Split('/')[-1] + $downloadLocation = Join-Path $downloadFolder $downloadFile + $savedVersionTxt = Join-Path $downloadFolder "downloaded_version.txt" + + if (Is-Work-Necessary $version $downloadFolder) { + Write-Host "Commencing installation of `"$Version`" to `"$downloadFolder`" from $downloadUrl." + Invoke-WebRequest -Uri $downloadUrl -OutFile $downloadLocation + + if ($downloadFile -like "*.zip") { + Expand-Archive -Path $downloadLocation -DestinationPath $downloadFolder -Force + } elseif ($downloadFile -like "*.tar.gz") { + tar -xzf $downloadLocation -C $downloadFolder + } else { + throw "Unsupported file format" + } + + # Remove the downloaded file after extraction + Remove-Item -Path $downloadLocation -Force + + # Record downloaded version + Set-Content -Path $savedVersionTxt -Value $Version + + # Set executable permissions if on macOS (Darwin) + $executable_path = Join-Path $downloadFolder $systemDetails.executable + if ($IsMacOS) { + chmod 755 $executable_path + } + } + else { + Write-Host "Target version `"$Version`" already present in target directory `"$downloadFolder.`"" + } +}