From 1eaa5681d3e99a86a99ac2a11223748f311ca170 Mon Sep 17 00:00:00 2001 From: Gianni Trevisiol Date: Thu, 21 Mar 2024 14:08:57 -0700 Subject: [PATCH] Refactor all deployments to use the MSI package. (#3284) * draft * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * wip * test * wip * wip * test * test * wip * test * wip * wip * wip * test * wip * wip * fix debug installation * update VM install doc * wip * wip * wip * wip * wip * improve msi log readability * wip * debug * nits * feedback * nl * updates * add extra stop-check * wip * wip * nit * fix * doc * exclude doc * sync --- .github/workflows/dependency-review.yml | 3 + .pre-commit-config.yaml | 3 + scripts/check_msi_installation.ps1 | 5 +- scripts/cleanup_ebpf_cicd_tests.ps1 | 8 + scripts/config_test_vm.psm1 | 86 +++-- scripts/execute_ebpf_cicd_tests.ps1 | 4 +- scripts/install_ebpf.psm1 | 401 ++++++++++++++---------- scripts/run_driver_tests.psm1 | 18 +- scripts/setup_ebpf_cicd_tests.ps1 | 1 + 9 files changed, 326 insertions(+), 203 deletions(-) diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 1cadba96e1..977970adec 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -1,3 +1,6 @@ +# Copyright (c) Microsoft Corporation +# SPDX-License-Identifier: MIT + # Dependency Review Action # # This Action will scan dependency manifest files that change as part of a Pull Request, diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ed03062335..6de2fbe44a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,3 +1,6 @@ +# Copyright (c) Microsoft Corporation +# SPDX-License-Identifier: MIT + repos: - repo: https://github.com/gitleaks/gitleaks rev: v8.16.3 diff --git a/scripts/check_msi_installation.ps1 b/scripts/check_msi_installation.ps1 index 09edbe3091..85d41d6023 100644 --- a/scripts/check_msi_installation.ps1 +++ b/scripts/check_msi_installation.ps1 @@ -75,10 +75,9 @@ function Install-MsiPackage { $res = $true - $arguments = "/i $MsiPath /qn /norestart /log msi-install.log $MsiAdditionalArguments" + $arguments = "/i $MsiPath /qn /norestart /l*v msi-install.log $MsiAdditionalArguments" Write-Host "Installing MSI package with arguments: '$arguments'..." $process = Start-Process -FilePath msiexec.exe -ArgumentList $arguments -Wait -PassThru - if ($process.ExitCode -eq 0) { Write-Host "Installation successful!" } else { @@ -105,7 +104,7 @@ function Uninstall-MsiPackage { Write-Host "Uninstalling MSI package..." $res = $true - $process = Start-Process -FilePath msiexec.exe -ArgumentList "/x $MsiPath /qn /norestart /log msi-uninstall.log" -Wait -PassThru + $process = Start-Process -FilePath msiexec.exe -ArgumentList "/x $MsiPath /qn /norestart /l*v msi-uninstall.log" -Wait -PassThru if ($process.ExitCode -eq 0) { Write-Host "Uninstallation successful!" } else { diff --git a/scripts/cleanup_ebpf_cicd_tests.ps1 b/scripts/cleanup_ebpf_cicd_tests.ps1 index 60d416d2af..29d440222b 100644 --- a/scripts/cleanup_ebpf_cicd_tests.ps1 +++ b/scripts/cleanup_ebpf_cicd_tests.ps1 @@ -15,11 +15,19 @@ $TestVMCredential = Get-StoredCredential -Target $Target -ErrorAction Stop # Load other utility modules. Import-Module .\common.psm1 -Force -ArgumentList ($LogFileName) -WarningAction SilentlyContinue Import-Module .\config_test_vm.psm1 -Force -ArgumentList ($TestVMCredential.UserName, $TestVMCredential.Password, $WorkingDirectory, $LogFileName) -WarningAction SilentlyContinue +Import-Module .\install_ebpf.psm1 -ArgumentList ($WorkingDirectory, $LogFileName) -Force -WarningAction SilentlyContinue # Read the test execution json. $TestExecutionConfig = Get-Content ("{0}\{1}" -f $PSScriptRoot, $TestExecutionJsonFileName) | ConvertFrom-Json $VMList = $TestExecutionConfig.VMMap.$SelfHostedRunnerName +# Uninstall eBPF Components on the test VM. +foreach($VM in $VMList) { + $VMName = $VM.Name + Write-Host "Uninstalling eBPF components on VM $VMName..." + Uninstall-eBPFComponentsOnVM -VMName $VMname -ErrorAction Stop +} + # Import logs from VMs. Import-ResultsFromVM -VMList $VMList -KmTracing $KmTracing diff --git a/scripts/config_test_vm.psm1 b/scripts/config_test_vm.psm1 index 9d098a4d7e..af87aecf5f 100644 --- a/scripts/config_test_vm.psm1 +++ b/scripts/config_test_vm.psm1 @@ -179,6 +179,7 @@ function Export-BuildArtifactsToVMs &tar @("cfz", "$tempFileName", "*") Write-Log "Created $tempFileName containing files in $pwd" + # Copy artifacts to the given VM list. foreach($VM in $VMList) { $VMName = $VM.Name $TestCredential = New-Credential -Username $Admin -AdminPassword $AdminPassword @@ -210,6 +211,52 @@ function Export-BuildArtifactsToVMs Remove-Item -Force $tempFileName } +# +# Install eBPF components on VM. +# + +function Install-eBPFComponentsOnVM +{ + param([parameter(Mandatory=$true)][string] $VMName, + [parameter(Mandatory=$true)][bool] $KmTracing, + [parameter(Mandatory=$true)][string] $KmTraceType) + + Write-Log "Installing eBPF components on $VMName" + $TestCredential = New-Credential -Username $Admin -AdminPassword $AdminPassword + + Invoke-Command -VMName $VMName -Credential $TestCredential -ScriptBlock { + param([Parameter(Mandatory=$True)] [string] $WorkingDirectory, + [Parameter(Mandatory=$True)] [string] $LogFileName, + [Parameter(Mandatory=$true)] [bool] $KmTracing, + [Parameter(Mandatory=$true)] [string] $KmTraceType) + $WorkingDirectory = "$env:SystemDrive\$WorkingDirectory" + Import-Module $WorkingDirectory\common.psm1 -ArgumentList ($LogFileName) -Force -WarningAction SilentlyContinue + Import-Module $WorkingDirectory\install_ebpf.psm1 -ArgumentList ($WorkingDirectory, $LogFileName) -Force -WarningAction SilentlyContinue + + Install-eBPFComponents -KmTracing $KmTracing -KmTraceType $KmTraceType -KMDFVerifier $true -ErrorAction Stop + } -ArgumentList ("eBPF", $LogFileName, $KmTracing, $KmTraceType) -ErrorAction Stop + Write-Log "eBPF components installed on $VMName" -ForegroundColor Green +} + +function Uninstall-eBPFComponentsOnVM +{ + param([parameter(Mandatory=$true)][string] $VMName) + + Write-Log "Unnstalling eBPF components on $VMName" + $TestCredential = New-Credential -Username $Admin -AdminPassword $AdminPassword + + Invoke-Command -VMName $VMName -Credential $TestCredential -ScriptBlock { + param([Parameter(Mandatory=$True)] [string] $WorkingDirectory, + [Parameter(Mandatory=$True)] [string] $LogFileName) + $WorkingDirectory = "$env:SystemDrive\$WorkingDirectory" + Import-Module $WorkingDirectory\common.psm1 -ArgumentList ($LogFileName) -Force -WarningAction SilentlyContinue + Import-Module $WorkingDirectory\install_ebpf.psm1 -ArgumentList ($WorkingDirectory, $LogFileName) -Force -WarningAction SilentlyContinue + + Uninstall-eBPFComponents + } -ArgumentList ("eBPF", $LogFileName) -ErrorAction Stop + Write-Log "eBPF components uninstalled on $VMName" -ForegroundColor Green +} + function ArchiveKernelModeDumpOnVM { param ( @@ -331,7 +378,6 @@ function Import-ResultsFromVM if (!(Test-Path ".\TestLogs\$VMName\Logs")) { New-Item -ItemType Directory -Path ".\TestLogs\$VMName\Logs" } - $VMTemp = Invoke-Command -Session $VMSession -ScriptBlock {return $Env:TEMP} Write-Log ("Copy $LogFileName from $VMTemp on $VMName to $pwd\TestLogs") Copy-Item ` @@ -436,30 +482,9 @@ function Import-ResultsFromVM Move-Item "$env:TEMP\$LogFileName" -Destination ".\TestLogs" -Force -ErrorAction Ignore 2>&1 | Write-Log } -function Install-eBPFComponentsOnVM -{ - param([parameter(Mandatory=$true)][string] $VMName, - [parameter(Mandatory=$true)][bool] $KmTracing, - [parameter(Mandatory=$true)][string] $KmTraceType) - - Write-Log "Installing eBPF components on $VMName" - $TestCredential = New-Credential -Username $Admin -AdminPassword $AdminPassword - - Invoke-Command -VMName $VMName -Credential $TestCredential -ScriptBlock { - param([Parameter(Mandatory=$True)] [string] $WorkingDirectory, - [Parameter(Mandatory=$True)] [string] $LogFileName, - [Parameter(Mandatory=$true)] [bool] $KmTracing, - [Parameter(Mandatory=$true)] [string] $KmTraceType) - $WorkingDirectory = "$env:SystemDrive\$WorkingDirectory" - Import-Module $WorkingDirectory\common.psm1 -ArgumentList ($LogFileName) -Force -WarningAction SilentlyContinue - Import-Module $WorkingDirectory\install_ebpf.psm1 -ArgumentList ($WorkingDirectory, $LogFileName) -Force -WarningAction SilentlyContinue - - Install-eBPFComponents -KmTracing $KmTracing -KmTraceType $KmTraceType -KMDFVerifier $true - Enable-KMDFVerifier - } -ArgumentList ("eBPF", $LogFileName, $KmTracing, $KmTraceType) -ErrorAction Stop - Write-Log "eBPF components installed on $VMName" -ForegroundColor Green -} - +# +# Configure network adapters on VMs. +# function Initialize-NetworkInterfacesOnVMs { param([parameter(Mandatory=$true)] $VMMap) @@ -535,3 +560,14 @@ function Get-Duonic { Move-Item -Path "$DownloadPath\corenet-ci-main\vm-setup\notmyfault64.exe" -Destination $pwd -Force Remove-Item -Path $DownloadPath -Force -Recurse } + +# Download the Visual C++ Redistributable. +function Get-VCRedistributable { + $url = "https://aka.ms/vs/16/release/vc_redist.x64.exe" + $DownloadPath = "$pwd\vc-redist" + mkdir $DownloadPath + Write-Host "Downloading Visual C++ Redistributable from $url to $DownloadPath" + Invoke-WebRequest -Uri $url -OutFile "$DownloadPath\vc_redist.x64.exe" + Move-Item -Path "$DownloadPath\vc_redist.x64.exe" -Destination $pwd -Force + Remove-Item -Path $DownloadPath -Force -Recurse +} \ No newline at end of file diff --git a/scripts/execute_ebpf_cicd_tests.ps1 b/scripts/execute_ebpf_cicd_tests.ps1 index 148541c2fb..64a4efdb66 100644 --- a/scripts/execute_ebpf_cicd_tests.ps1 +++ b/scripts/execute_ebpf_cicd_tests.ps1 @@ -20,8 +20,8 @@ $AdminTestVMCredential = Get-StoredCredential -Target $AdminTarget -ErrorAction $StandardUserTestVMCredential = Get-StoredCredential -Target $StandardUserTarget -ErrorAction Stop # Load other utility modules. -Import-Module .\common.psm1 -Force -ArgumentList ($LogFileName) -WarningAction SilentlyContinue -Import-Module .\vm_run_tests.psm1 ` +Import-Module $PSScriptRoot\common.psm1 -Force -ArgumentList ($LogFileName) -WarningAction SilentlyContinue +Import-Module $PSScriptRoot\vm_run_tests.psm1 ` -Force ` -ArgumentList ( $AdminTestVMCredential.UserName, diff --git a/scripts/install_ebpf.psm1 b/scripts/install_ebpf.psm1 index 70eb93733b..213c927619 100644 --- a/scripts/install_ebpf.psm1 +++ b/scripts/install_ebpf.psm1 @@ -5,129 +5,60 @@ param ([Parameter(Mandatory=$True)] [string] $WorkingDirectory, [Parameter(Mandatory=$True)] [string] $LogFileName) Push-Location $WorkingDirectory - -$BinaryPath = "$Env:systemroot\system32"; - Import-Module $PSScriptRoot\common.psm1 -Force -ArgumentList ($LogFileName) -WarningAction SilentlyContinue -# eBPF Drivers. -$EbpfDrivers = -@{ - "EbpfCore" = "ebpfcore.sys"; - "NetEbpfExt" = "netebpfext.sys"; - "SampleEbpfExt" = "sample_ebpf_ext.sys" -} +$VcRedistPath = Join-Path $WorkingDirectory "vc_redist.x64.exe" +$MsiPath = Join-Path $WorkingDirectory "ebpf-for-windows.msi" -# -# Uninstall eBPF components. -# -function Unregister-eBPFComponents -{ - # Uninstall drivers. - $EbpfDrivers.GetEnumerator() | ForEach-Object { - # New-Service does not support installing drivers. - sc.exe delete $_.Name 2>&1 | Write-Log +# eBPF drivers and services. +$EbpfDrivers = @{ + "EbpfCore" = [PSCustomObject]@{ + "Name" = "ebpfcore.sys" + "IsDriver" = $true + "InstalledByMsi" = $true } - - # Uninstall user mode service. - sc.exe delete eBPFSvc 2>&1 | Write-Log - - # Delete the eBPF netsh helper. - netsh delete helper ebpfnetsh.dll 2>&1 | Write-Log - - # Execute "export_program_info.exe --clear" - if (Test-Path -Path "export_program_info.exe") { - .\export_program_info.exe --clear - if ($LASTEXITCODE -ne 0) { - throw ("Failed to run 'export_program_info.exe --clear'."); - } else { - Write-Log "'export_program_info.exe --clear' succeeded." -ForegroundColor Green - } + "NetEbpfExt" = [PSCustomObject]@{ + "Name" = "netebpfext.sys" + "IsDriver" = $true + "InstalledByMsi" = $true } - - # Execute "export_program_info_sample.exe --clear" - if (Test-Path -Path "export_program_info_sample.exe") { - .\export_program_info_sample.exe --clear - if ($LASTEXITCODE -ne 0) { - throw ("Failed to run 'export_program_info_sample.exe --clear'."); - } else { - Write-Log "'export_program_info_sample.exe --clear' succeeded." -ForegroundColor Green - } + "SampleEbpfExt" = [PSCustomObject]@{ + "Name" = "sample_ebpf_ext.sys" + "IsDriver" = $true + "InstalledByMsi" = $false } -} - -# -# Install eBPF components. -# - -function Register-eBPFComponents -{ - # Uninstall previous installations (if any). - Unregister-eBPFComponents - - # Export program info. - if (Test-Path -Path "export_program_info.exe") { - .\export_program_info.exe - if ($LASTEXITCODE -ne 0) { - throw ("Failed to run 'export_program_info.exe'."); - } else { - Write-Log "'export_program_info.exe' succeeded." -ForegroundColor Green - } + "EbpfSvc" = [PSCustomObject]@{ + "Name" = "ebpfsvc.exe" + "IsDriver" = $false + "InstalledByMsi" = $true } - if (Test-Path -Path "export_program_info_sample.exe") { - .\export_program_info_sample.exe - if ($LASTEXITCODE -ne 0) { - throw ("Failed to run 'export_program_info_sample.exe'."); - } else { - Write-Log "'export_program_info_sample.exe' succeeded." -ForegroundColor Green - } - } - - # Install drivers. - $EbpfDrivers.GetEnumerator() | ForEach-Object { - if (Test-Path -Path ("$BinaryPath\{0}" -f $_.Value)) { - Write-Log ("Installing {0}..." -f $_.Name) -ForegroundColor Green - # New-Service does not support installing drivers. - sc.exe create $_.Name type=kernel start=demand binpath=("$BinaryPath\{0}" -f $_.Value) 2>&1 | Write-Log - if ($LASTEXITCODE -ne 0) { - throw ("Failed to create $_.Name driver.") - } else { - Write-Log ("{0} driver created." -f $_.Name) -ForegroundColor Green - } - } - if (Test-Path -Path ("$BinaryPath\drivers\{0}" -f $_.Value)) { - Write-Log ("Installing {0}..." -f $_.Name) -ForegroundColor Green - # New-Service does not support installing drivers. - sc.exe create $_.Name type=kernel start=demand binpath=("$BinaryPath\drivers\{0}" -f $_.Value) 2>&1 | Write-Log - if ($LASTEXITCODE -ne 0) { - throw ("Failed to create $_.Name driver.") - } else { - Write-Log ("{0} driver created." -f $_.Name) -ForegroundColor Green - } - } - } - - # Install user mode service. - if (Test-Path -Path "ebpfsvc.exe") { - .\eBPFSvc.exe install 2>&1 | Write-Log - if ($LASTEXITCODE -ne 0) { - throw ("Failed to create eBPF user mode service.") - } else { - Write-Log "eBPF user mode service created." -ForegroundColor Green - } - } - - # Add the eBPF netsh helper. - netsh add helper ebpfnetsh.dll 2>&1 | Write-Log } +# eBPF Debug Runtime DLLs. +$VCDebugRuntime = @( + "concrt140d.dll", + "msvcp140d.dll", + "msvcp140d_atomic_wait.dll", + "msvcp140d_codecvt_ids.dll", + "msvcp140_1d.dll", + "msvcp140_2d.dll", + "vccorlib140d.dll", + "vcruntime140d.dll", + "vcruntime140_1d.dll", + "vcruntime140_threadsd.dll", + "ucrtbased.dll" +) + function Enable-KMDFVerifier { - # Install drivers. + # Enable KMDF verifier for the eBPF drivers. $EbpfDrivers.GetEnumerator() | ForEach-Object { - New-Item -Path ("HKLM:\System\CurrentControlSet\Services\{0}\Parameters\Wdf" -f $_.Name) -Force -ErrorAction Stop - New-ItemProperty -Path ("HKLM:\System\CurrentControlSet\Services\{0}\Parameters\Wdf" -f $_.Name) -Name "VerifierOn" -Value 1 -PropertyType DWord -Force -ErrorAction Stop - New-ItemProperty -Path ("HKLM:\System\CurrentControlSet\Services\{0}\Parameters\Wdf" -f $_.Name) -Name "TrackHandles" -Value "*" -PropertyType MultiString -Force -ErrorAction Stop + if ($_.Value.IsDriver) { + Write-Log("Enabling KMDF verifier for $($_.Key)...") + New-Item -Path ("HKLM:\System\CurrentControlSet\Services\{0}\Parameters\Wdf" -f $_.Name) -Force -ErrorAction Stop + New-ItemProperty -Path ("HKLM:\System\CurrentControlSet\Services\{0}\Parameters\Wdf" -f $_.Name) -Name "VerifierOn" -Value 1 -PropertyType DWord -Force -ErrorAction Stop + New-ItemProperty -Path ("HKLM:\System\CurrentControlSet\Services\{0}\Parameters\Wdf" -f $_.Name) -Name "TrackHandles" -Value "*" -PropertyType MultiString -Force -ErrorAction Stop + } } } @@ -139,7 +70,7 @@ function Start-WPRTrace param([parameter(Mandatory=$true)][bool] $KmTracing, [parameter(Mandatory=$true)][string] $KmTraceType) - Write-Log ("kernel mode ETW tracing: " + $KmTracing) + Write-Log("kernel mode ETW tracing: " + $KmTracing) if ($KmTracing) { if ($KmTraceType -eq "file") { @@ -157,14 +88,14 @@ function Start-WPRTrace } if ($ProcInfo.ExitCode -ne 0) { - Write-log ("wpr.exe start ETL trace failed. Exit code: " + $ProcInfo.ExitCode) + Write-Log("wpr.exe start ETL trace failed. Exit code: " + $ProcInfo.ExitCode) Write-log "wpr.exe (start) error output: " foreach ($line in get-content -Path .\StdErr.txt) { - write-log ( "`t" + $line) + Write-Log( "`t" + $line) } throw "Start ETL trace failed." } - Write-Log ("Start ETL trace success. wpr.exe exit code: " + $ProcInfo.ExitCode + "`n") + Write-Log("Start ETL trace success. wpr.exe exit code: " + $ProcInfo.ExitCode + "`n") Write-Log "Query ETL tracing status after trace start" $ProcInfo = Start-Process -FilePath "wpr.exe" ` @@ -172,44 +103,55 @@ function Start-WPRTrace -NoNewWindow -Wait -PassThru ` -RedirectStandardOut .\StdOut.txt -RedirectStandardError .\StdErr.txt if ($ProcInfo.ExitCode -ne 0) { - Write-log ("wpr.exe query ETL trace status failed. Exit code: " + $ProcInfo.ExitCode) + Write-Log("wpr.exe query ETL trace status failed. Exit code: " + $ProcInfo.ExitCode) Write-log "wpr.exe (query) error output: " foreach ($line in get-content -Path .\StdErr.txt) { - write-log ( "`t" + $line) + Write-Log( "`t" + $line) } throw "Query ETL trace status failed." } else { Write-log "wpr.exe (query) results: " foreach ($line in get-content -Path .\StdOut.txt) { - write-log ( " `t" + $line) + Write-Log( " `t" + $line) } } - Write-Log ("Query ETL trace status success. wpr.exe exit code: " + $ProcInfo.ExitCode + "`n" ) + Write-Log("Query ETL trace status success. wpr.exe exit code: " + $ProcInfo.ExitCode + "`n" ) } } -# -# Start service and drivers. -# -function Start-eBPFComponents -{ - param([parameter(Mandatory=$true)] [bool] $KmTracing, - [parameter(Mandatory=$true)] [string] $KmTraceType) - - Start-WPRTrace -KmTracing $KmTracing -KmTraceType $KmTraceType - - # Start drivers. +# This function specifically tests that all eBPF drivers and services can be stopped. +function Stop-eBPFComponents { + # First, stop user mode service, so that EbpfCore does not hang on stop. + if (Get-Service "eBPFSvc" -ErrorAction SilentlyContinue) { + try { + Stop-Service "eBPFSvc" -ErrorAction Stop 2>&1 | Write-Log + Write-Log "eBPFSvc service stopped." -ForegroundColor Green + } catch { + throw "Failed to stop 'eBPFSvc' service: $_." + } + } else { + Write-Log "'eBPFSvc' service is not present (i.e., release build), skipping stopping." -ForegroundColor Green + } + # Stop the drivers and services. $EbpfDrivers.GetEnumerator() | ForEach-Object { - if (Test-Path -Path ("$BinaryPath\drivers\{0}" -f $_.Value)) { - Start-Service $_.Name -ErrorAction Stop | Write-Log - Write-Host ("{0} Driver started." -f $_.Name) + try { + if ($_.Value.IsDriver) { + Stop-Service $_.Name -ErrorAction Stop 2>&1 | Write-Log + Write-Log "$($_.Key) driver stopped." -ForegroundColor Green + } + } catch { + throw "Failed to stop $($_.Key) driver: $_." } } +} - if (Test-Path -Path "ebpfsvc.exe") { - # Start user mode service. - Start-Service "eBPFSvc" -ErrorAction Stop | Write-Log - Write-Host "eBPFSvc service started." +function Print-eBPFComponentsStatus([string] $message = "") +{ + # Print the status of the eBPF drivers and services. + Write-Log($message) + $EbpfDrivers.GetEnumerator() | ForEach-Object { + Write-Log "Querying the status of $($_.Key)..." + sc.exe query $_.Key 2>&1 | Write-Log } } @@ -219,48 +161,179 @@ function Install-eBPFComponents [parameter(Mandatory=$true)] [string] $KmTraceType, [parameter(Mandatory=$false)] [bool] $KMDFVerifier = $false) - # Stop eBPF Components - Stop-eBPFComponents + # Print the status of the eBPF drivers and services before installation. + # This is useful for detecting issues with the runner baselines. + Print-eBPFComponentsStatus "Querying the status of eBPF drivers and services before the installation (none should be present)..." | Out-Null + + # Install the Visual C++ Redistributable (Release version, which is required for the MSI installation). + Write-Log("Installing Visual C++ Redistributable from '$VcRedistPath'...") + $process = Start-Process -FilePath $VcRedistPath -ArgumentList "/quiet", "/norestart" -Wait -PassThru + if ($process.ExitCode -ne 0) { + Write-Log("Visual C++ Redistributable installation FAILED. Exit code: $($process.ExitCode).") -ForegroundColor Red + throw ("Visual C++ Redistributable installation FAILED. Exit code: $($process.ExitCode).") + } + Write-Log("Cleaning up...") + Remove-Item $VcRedistPath -Force + Write-Log("Visual C++ Redistributable installation completed successfully!") -ForegroundColor Green + + # Copy the VC debug runtime DLLs to the system32 directory, + # so that debug versions of the MSI can be installed (i.e., export_program_info.exe will not fail). + Write-Log("Copying VC debug runtime DLLs to the $system32Path directory...") + # Test if the VC debug runtime DLLs are present in the working directory (indicating a debug build). + $VCDebugRuntime = $VCDebugRuntime | Where-Object { Test-Path (Join-Path $WorkingDirectory $_) } + if (-not $VCDebugRuntime) { + Write-Log("VC debug runtime DLLs not found in the working directory (i.e., release build). Skipping this step.") -ForegroundColor Yellow + } else { + $system32Path = Join-Path $env:SystemRoot "System32" + $VCDebugRuntime | ForEach-Object { + $sourcePath = Join-Path $WorkingDirectory $_ + $destinationPath = Join-Path $system32Path $_ + Write-Log("Copying '$sourcePath' to '$destinationPath'...") + Copy-Item -Path $sourcePath -Destination $destinationPath -Force + } + Write-Log("VC debug runtime DLLs copied successfully!") -ForegroundColor Green + } + + # Install the MSI package. + $arguments = "/i $MsiPath ADDLOCAL=ALL /qn /norestart /l*v msi-install.log" + Write-Log("Installing the eBPF MSI package: 'msiexec.exe $arguments'...") + $process = Start-Process -FilePath msiexec.exe -ArgumentList $arguments -Wait -PassThru + if ($process.ExitCode -ne 0) { + Write-Log("MSI installation FAILED. Exit code: $($process.ExitCode).") -ForegroundColor Red + + # For clear readability within the CICD pipeline and final uploaded log output, + # read each line of the log file and print it (otherwise all the log content is printed as a single line). + Write-Log("Contents of msi-install.log:") + Get-Content -Path "msi-install.log" | ForEach-Object { + Write-Log($_) + } + throw ("MSI installation FAILED. Exit code: $($process.ExitCode).") + } + Write-Log("eBPF MSI installation completed successfully!") -ForegroundColor Green - # Copy all binaries to system32. - Copy-Item *.sys -Destination "$Env:systemroot\system32\drivers" -Force -ErrorAction Stop 2>&1 | Write-Log - if (Test-Path -Path "drivers") { - Copy-Item drivers\*.sys -Destination "$Env:systemroot\system32\drivers" -Force -ErrorAction Stop 2>&1 | Write-Log + # Install the extra drivers that are not installed by the MSI package. + $EbpfDrivers.GetEnumerator() | ForEach-Object { + if (-not $_.Value.InstalledByMsi) { + $driverPath = if (Test-Path -Path ("$pwd\{0}" -f $_.Value.Name)) { + "$pwd\{0}" -f $_.Value.Name + } elseif (Test-Path -Path ("$pwd\drivers\{0}" -f $_.Value.Name)) { + "$pwd\drivers\{0}" -f $_.Value.Name + } else { + throw ("Driver file not found for $($_.Key).") + } + Write-Log("Installing $($_.Key)...") -ForegroundColor Green + sc.exe create $_.Key type=kernel start=demand binpath=$driverPath 2>&1 | Write-Log + if ($LASTEXITCODE -ne 0) { + throw ("Failed to create $($_.Key) driver.") + } else { + Write-Log("$($_.Key) driver created.") -ForegroundColor Green + # Start the service. + Write-Log("Starting $($_.Key) service...") -ForegroundColor Green + sc.exe start $_.Key 2>&1 | Write-Log + if ($LASTEXITCODE -ne 0) { + throw ("Failed to start $($_.Key) service.") + } else { + Write-Log("$($_.Key) service started.") -ForegroundColor Green + } + } + } } - if (Test-Path -Path "testing\testing") { - Copy-Item testing\testing\*.sys -Destination "$Env:systemroot\system32\drivers" -Force -ErrorAction Stop 2>&1 | Write-Log + + # Export program info for the sample driver. + Write-Log("Running 'export_program_info_sample.exe'...") + if (Test-Path -Path "export_program_info_sample.exe") { + .\export_program_info_sample.exe 2>&1 | Write-Log + if ($LASTEXITCODE -ne 0) { + throw ("Failed to run 'export_program_info_sample.exe'."); + } else { + Write-Log "'export_program_info_sample.exe' succeeded." -ForegroundColor Green + } } - Copy-Item *.dll -Destination "$Env:systemroot\system32" -Force -ErrorAction Stop 2>&1 | Write-Log - Copy-Item *.exe -Destination "$Env:systemroot\system32" -Force -ErrorAction Stop 2>&1 | Write-Log - # Register all components. - Register-eBPFComponents + # Print the status of the eBPF drivers and services after installation. + Print-eBPFComponentsStatus "Verifying the status of eBPF drivers and services after the installation..." | Out-Null + # Optionally enable KMDF verifier and tag tracking. if ($KMDFVerifier) { - # Enable KMDF verifier and tag tracking. Enable-KMDFVerifier } - # Start all components. - Start-eBPFComponents -KmTracing $KmTracing -KmTraceType $KmTraceType + # Start KM tracing. + Start-WPRTrace -KmTracing $KmTracing -KmTraceType $KmTraceType } -function Stop-eBPFComponents +function Uninstall-eBPFComponents { - # Stop user mode service. - Stop-Service "eBPFSvc" -ErrorAction Ignore 2>&1 | Write-Log + # This section double-checks that all drivers and services are stopped before proceeding with uninstallation. + # It iterates through each driver and service, retrieving its status, and if any service is found to be running, it throws an error. + $allStopped = $true + if (Get-Service "eBPFSvc" -ErrorAction SilentlyContinue) { + $serviceStatus = (Get-Service "eBPFSvc").Status + if ($serviceStatus -ne "Stopped") { + Write-Log "eBPFSvc service is not stopped." -ForegroundColor Red + $allStopped = $false + } + Write-Log "eBPFSvc service stopped." -ForegroundColor Green + } else { + Write-Log "'eBPFSvc' service is not present (i.e., release build), skipping stopping." -ForegroundColor Green + } + $EbpfDrivers.GetEnumerator() | ForEach-Object { + if ($_.Value.IsDriver) { + $driverStatus = (Get-Service $_.Key).Status + if ($driverStatus -ne "Stopped") { + Write-Log "$($_.Key) driver is not stopped." -ForegroundColor Red + $allStopped = $false + } + } + } + if (-not $allStopped) { + throw "One or more services are not stopped." + } - # Stop the drivers. + # Firstly, uninstall the extra drivers that are not installed by the MSI package. $EbpfDrivers.GetEnumerator() | ForEach-Object { - Stop-Service $_.Name -ErrorAction Ignore 2>&1 | Write-Log + if (-not $_.Value.InstalledByMsi) { + Write-Log("Deleting $($_.Key) service...") -ForegroundColor Green + sc.exe delete $_.Key 2>&1 | Write-Log + if ($LASTEXITCODE -ne 0) { + throw ("Failed to delete $($_.Key) service.") + } else { + Write-Log("$($_.Key) service deleted.") -ForegroundColor Green + } + } } -} -function Uninstall-eBPFComponents -{ - Stop-eBPFComponents - Unregister-eBPFComponents - Remove-Item "$Env:systemroot\system32\drivers\*bpf*" -Force -ErrorAction Stop 2>&1 | Write-Log - Remove-Item "$Env:systemroot\system32\*bpf*" -Force -ErrorAction Stop 2>&1 | Write-Log + # Clear export program info for the sample driver. + Write-Log("Running 'export_program_info_sample.exe --clear'...") + if (Test-Path -Path "export_program_info_sample.exe --clear") { + .\export_program_info_sample.exe --clear + if ($LASTEXITCODE -ne 0) { + throw ("Failed to run 'export_program_info_sample.exe --clear'.") + } else { + Write-Log("'export_program_info_sample.exe --clear' succeeded.") -ForegroundColor Green + } + } + Write-Log("Clearing export program info for the sample driver completed successfully!") -ForegroundColor Green + + # Uninstall the MSI package. + $arguments = "/x $MsiPath /qn /norestart /l*v msi-uninstall.log" + Write-Log("Uninstalling eBPF MSI package at 'msiexec.exe $arguments'...") + $process = Start-Process -FilePath msiexec.exe -ArgumentList $arguments -Wait -PassThru + if ($process.ExitCode -eq 0) { + Write-Log("Uninstallation successful!") -ForegroundColor Green + } else { + Write-Log("Uninstallation FAILED. Exit code: $($process.ExitCode)") -ForegroundColor Red + + # For clear readability within the CICD pipeline and final uploaded log output, + # read each line of the log file and print it (otherwise all the log content is printed as a single line). + Write-Log("Contents of msi-uninstall.log:") + Get-Content -Path "msi-uninstall.log" | ForEach-Object { + Write-Log($_) + } + throw ("MSI uninstallation FAILED. Exit code: $($process.ExitCode).") + } + Write-Log("MSI uninstallation completed successfully!") -ForegroundColor Green + + # Stop KM tracing. wpr.exe -cancel } diff --git a/scripts/run_driver_tests.psm1 b/scripts/run_driver_tests.psm1 index e28d0c2460..26bf9a10e4 100644 --- a/scripts/run_driver_tests.psm1 +++ b/scripts/run_driver_tests.psm1 @@ -9,7 +9,7 @@ Push-Location $WorkingDirectory Import-Module .\common.psm1 -Force -ArgumentList ($LogFileName) -WarningAction SilentlyContinue Import-Module .\install_ebpf.psm1 -Force -ArgumentList ($WorkingDirectory, $LogFileName) -WarningAction SilentlyContinue -$CodeCoverage = 'C:\Program Files\OpenCppCoverage\OpenCppCoverage.exe' +$CodeCoverage = "$env:ProgramFiles\OpenCppCoverage\OpenCppCoverage.exe" # # Execute tests on VM. @@ -92,7 +92,7 @@ function Invoke-CICDTests ) - pushd $WorkingDirectory + Push-Location $WorkingDirectory $env:EBPF_ENABLE_WER_REPORT = "yes" $TestList = @( @@ -127,7 +127,7 @@ function Invoke-CICDTests Invoke-Test -TestName "ebpf_performance.exe" -VerboseLogs $VerboseLogs } - popd + Pop-Location } function Invoke-XDPTest @@ -140,7 +140,7 @@ function Invoke-XDPTest [parameter(Mandatory = $false)][string] $UserModeDumpFolder = "C:\Dumps" ) - pushd $WorkingDirectory + Push-Location $WorkingDirectory Write-Log "Executing $XDPTestName with remote address: $RemoteIPV4Address" $TestRunScript = ".\Run-Self-Hosted-Runner-Test.ps1" @@ -170,7 +170,7 @@ function Invoke-XDPTest Write-Log "$XDPTestName Test Passed" -ForegroundColor Green Write-Log "`n`n" - popd + Pop-Location } function Invoke-ConnectRedirectTest @@ -190,7 +190,7 @@ function Invoke-ConnectRedirectTest [parameter(Mandatory = $false)][int] $TestHangTimeout = 3600, [parameter(Mandatory = $false)][string] $UserModeDumpFolder = "C:\Dumps") - pushd $WorkingDirectory + Push-Location $WorkingDirectory $TestRunScript = ".\Run-Self-Hosted-Runner-Test.ps1" $TestCommand = ".\connect_redirect_tests.exe" @@ -262,7 +262,7 @@ function Invoke-ConnectRedirectTest Write-Log "Connect-Redirect Test Passed" -ForegroundColor Green - popd + Pop-Location } function Invoke-CICDStressTests @@ -274,7 +274,7 @@ function Invoke-CICDStressTests [parameter(Mandatory = $false)][bool] $NeedKernelDump = $true, [parameter(Mandatory = $false)][bool] $RestartExtension = $false) - pushd $WorkingDirectory + Push-Location $WorkingDirectory $env:EBPF_ENABLE_WER_REPORT = "yes" Write-Log "Executing eBPF kernel mode multi-threaded stress tests (restart extension:$RestartExtension)." @@ -298,7 +298,7 @@ function Invoke-CICDStressTests -NeedKernelDump $True ` -Verbose - popd + Pop-Location } function Invoke-CICDPerformanceTests diff --git a/scripts/setup_ebpf_cicd_tests.ps1 b/scripts/setup_ebpf_cicd_tests.ps1 index 5b8d1d863f..91593d84af 100644 --- a/scripts/setup_ebpf_cicd_tests.ps1 +++ b/scripts/setup_ebpf_cicd_tests.ps1 @@ -40,6 +40,7 @@ if ($TestMode -eq "CI/CD") { } Get-Duonic +Get-VCRedistributable # Export build artifacts to the test VMs. Export-BuildArtifactsToVMs -VMList $VMList -ErrorAction Stop