Skip to content
Merged
Show file tree
Hide file tree
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
4 changes: 4 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
linux_host/**/*.sh text eol=lf
linux_host/**/*.py text eol=lf
custom_script_extensions/*.sh text eol=lf
custom_script_extensions/*.py text eol=lf
216 changes: 216 additions & 0 deletions .github/workflows/broker-authorization-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
name: Broker authorization and lifecycle

on:
pull_request:
paths:
- "api/**"
- "sql_queries/**"
- "linux_host/**"
- "avd_host/**"
- "deploy/**"
- "custom_script_extensions/**"
- "front_end/*.py"
- "front_end/requirements*.txt"
- "tests/**"
- ".gitattributes"
- ".github/workflows/broker-authorization-tests.yml"
push:
paths:
- "api/**"
- "sql_queries/**"
- "linux_host/**"
- "avd_host/**"
- "deploy/**"
- "custom_script_extensions/**"
- "front_end/*.py"
- "front_end/requirements*.txt"
- "tests/**"
- ".gitattributes"
- ".github/workflows/broker-authorization-tests.yml"
workflow_dispatch:

permissions:
contents: read

concurrency:
group: broker-authorization-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
api-and-host:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.13"
cache: pip
cache-dependency-path: |
api/requirements.txt
api/requirements-dev.txt
- name: Install API test dependencies
run: python -m pip install -r api/requirements.txt -r api/requirements-dev.txt
- name: Test API authorization and lifecycle
working-directory: api
run: python -m pytest
- name: Install isolated portal dependencies
run: |
python -m venv "$RUNNER_TEMP/portal-contract"
"$RUNNER_TEMP/portal-contract/bin/python" -m pip install -r front_end/requirements.txt -r front_end/requirements-dev.txt
- name: Verify real-JWT broker and portal HTTP contract
run: python tests/verify_portal_broker_contract.py --portal-python "$RUNNER_TEMP/portal-contract/bin/python"
- name: Parse Linux agent scripts
shell: python
run: |
from pathlib import Path
import subprocess

for root in ("linux_host", "custom_script_extensions"):
for script in sorted(Path(root).rglob("*.sh")):
subprocess.run(["bash", "-n", str(script)], check=True)
- name: Test Linux lease and mount behavior without host changes
run: python -m unittest discover -s linux_host/tests -v
- name: Test Linux deployment and workload probes without Azure
run: |
python deploy/tests/Test-HostCompatibility.py
python deploy/tests/Test-HostPrerequisites.py
python deploy/tests/Test-LegacyGateEnrollment.py
python deploy/tests/Test-IdleLease.py
python deploy/tests/Test-WorkloadReadiness.py

sql-transactions:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- name: Verify migrations and competing SQL transactions in isolated LocalDB
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$candidates = @(
"$env:ProgramFiles\Microsoft SQL Server\170\Tools\Binn\SqlLocalDB.exe",
"$env:ProgramFiles\Microsoft SQL Server\160\Tools\Binn\SqlLocalDB.exe",
"$env:ProgramFiles\Microsoft SQL Server\150\Tools\Binn\SqlLocalDB.exe"
)
$localDb = $candidates | Where-Object { Test-Path -LiteralPath $_ -PathType Leaf } | Select-Object -First 1
if (-not $localDb) {
throw 'SQL LocalDB is required; transaction tests cannot be skipped.'
}
$suffix = "${{ github.run_id }}_${{ github.run_attempt }}"
$instance = "LinuxBrokerAuth_$suffix"
$database = "LinuxBrokerAuthorizationTests_$suffix"
$created = $false
$databaseCreated = $false
$connectionString = "Data Source=(localdb)\$instance;Initial Catalog=master;Integrated Security=True;TrustServerCertificate=True;Connect Timeout=30"
try {
$instances = @(& $localDb info)
if ($LASTEXITCODE -ne 0 -or $instances -contains $instance) {
throw 'Cannot confirm that the isolated instance name is unused.'
}
& $localDb create $instance -s
if ($LASTEXITCODE -ne 0) {
throw 'Creating the isolated LocalDB instance failed.'
}
$created = $true
$connection = [System.Data.SqlClient.SqlConnection]::new($connectionString)
try {
$connection.Open()
$command = $connection.CreateCommand()
$command.CommandText = "IF DB_ID(N'$database') IS NOT NULL THROW 50000, 'Test database already exists.', 1; EXEC sp_configure 'contained database authentication', 1; RECONFIGURE; CREATE DATABASE [$database] CONTAINMENT=PARTIAL;"
[void]$command.ExecuteNonQuery()
$databaseCreated = $true
}
finally {
$connection.Dispose()
}
& .\sql_queries\tests\Run-LocalDbIntegration.ps1 `
-InstanceName $instance -DatabaseName $database -SqlLocalDbExe $localDb
& .\tests\Test-RuntimeDatabaseIntegration.ps1 `
-InstanceName $instance -DatabaseName $database -SqlLocalDbExe $localDb
}
finally {
if ($created) {
try {
if ($databaseCreated) {
$connection = [System.Data.SqlClient.SqlConnection]::new($connectionString)
try {
$connection.Open()
$command = $connection.CreateCommand()
$command.CommandText = "ALTER DATABASE [$database] SET SINGLE_USER WITH ROLLBACK IMMEDIATE; DROP DATABASE [$database];"
[void]$command.ExecuteNonQuery()
}
finally {
$connection.Dispose()
}
}
}
finally {
[System.Data.SqlClient.SqlConnection]::ClearAllPools()
& $localDb stop $instance
if ($LASTEXITCODE -ne 0) {
throw 'Stopping the isolated LocalDB instance failed.'
}
& $localDb delete $instance
if ($LASTEXITCODE -ne 0) {
throw 'Removing the isolated LocalDB instance failed.'
}
}
}
}

windows-launcher:
runs-on: windows-latest
defaults:
run:
shell: pwsh
working-directory: avd_host/broker
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
global-json-file: avd_host/broker/global.json
- name: Test and package the per-user launcher
run: |
$ErrorActionPreference = 'Stop'
$bundle = & .\Publish-Launcher.ps1 `
-OutputDirectory "$env:RUNNER_TEMP\broker-launcher" `
-DotNetPath (Get-Command dotnet -ErrorAction Stop).Source
if (-not (Test-Path -LiteralPath $bundle.BundlePath -PathType Leaf)) {
throw 'The publisher did not produce the installer bundle.'
}

deployment:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.13"
- name: Parse PowerShell scripts
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$failures = @()
foreach ($root in @('avd_host', 'deploy', 'custom_script_extensions', 'tests', 'sql_queries\tests')) {
foreach ($file in Get-ChildItem $root -Recurse -Filter *.ps1) {
$tokens = $null
$parseErrors = $null
[void][System.Management.Automation.Language.Parser]::ParseFile($file.FullName, [ref]$tokens, [ref]$parseErrors)
foreach ($parseError in $parseErrors) {
$failures += "$($file.FullName):$($parseError.Extent.StartLineNumber): $($parseError.Message)"
}
}
}
if ($failures.Count -gt 0) {
throw ($failures -join [Environment]::NewLine)
}
- name: Test deployment boundaries without Azure
shell: pwsh
run: .\deploy\tests\Test-Deployment.ps1
- name: Compile deployment templates
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
az bicep build --file .\deploy\bicep\main.bicep --outfile "$env:RUNNER_TEMP\linuxbroker-main.json"
if ($LASTEXITCODE -ne 0) {
throw 'Bicep compilation failed.'
}
Loading
Loading