Public/BuildkitTools.psm1
########################################################################### # # # Copyright (c) Microsoft Corporation. All rights reserved. # # # # This code is licensed under the MIT License (MIT). # # # ########################################################################### using module "..\Private\CommonToolUtilities.psm1" $ModuleParentPath = Split-Path -Parent $PSScriptRoot Import-Module -Name "$ModuleParentPath\Private\CommonToolUtilities.psm1" -Force function Get-BuildkitLatestVersion { $latestVersion = Get-LatestToolVersion -Tool "buildkit" return $latestVersion } function Install-Buildkit { [CmdletBinding( DefaultParameterSetName = 'Install', SupportsShouldProcess = $true, ConfirmImpact = 'High' )] param ( [Parameter(ParameterSetName = 'Install')] [Parameter(ParameterSetName = 'Setup')] [parameter(HelpMessage = "Buildkit version to use. Defaults to 'latest'")] [string]$Version = "latest", [Parameter(ParameterSetName = 'Install')] [Parameter(ParameterSetName = 'Setup')] [parameter(HelpMessage = "Path to install buildkit. Defaults to ~\program files\buildkit")] [string]$InstallPath = "$Env:ProgramFiles\Buildkit", [Parameter(ParameterSetName = 'Install')] [Parameter(ParameterSetName = 'Setup')] [parameter(HelpMessage = "Path to download files. Defaults to user's Downloads folder")] [string]$DownloadPath = "$HOME\Downloads", [Parameter(ParameterSetName = 'Setup')] [switch]$Setup, [Parameter(ParameterSetName = 'Setup')] [string]$WinCNIPath, [Parameter(HelpMessage = 'OS architecture to download files for. Default is $env:PROCESSOR_ARCHITECTURE')] [ValidateSet('amd64', '386', "arm", "arm64")] [string]$OSArchitecture = $env:PROCESSOR_ARCHITECTURE, [Parameter(ParameterSetName = 'Install')] [Parameter(ParameterSetName = 'Setup')] [parameter(HelpMessage = "Installs Buildkit even if the tool already exists at the specified path")] [Switch]$Force ) begin { # Check if Buildkit is alread installed $isInstalled = -not (Test-EmptyDirectory -Path $InstallPath) $WhatIfMessage = "Buildkit will be installed at $InstallPath" if ($isInstalled) { $WhatIfMessage = "Buildkit will be uninstalled from and reinstalled at $InstallPath" } if ($Setup) { <# Action when this condition is true #> $WhatIfMessage = "Buildkit will be installed at $InstallPath and buildkitd service will be registered and started" } } process { if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, $WhatIfMessage)) { # Check if tool already exists at specified location if ($isInstalled) { $errMsg = "Buildkit already exists at $InstallPath or the directory is not empty" Write-Warning $errMsg # Uninstall if tool exists at specified location. Requires user consent try { Uninstall-Buildkit -Path "$InstallPath" -Force:$Force -Confirm:$false | Out-Null } catch { Throw "Buildkit installation failed. $_" } } # Get Buildkit version to install if (!$Version) { $Version = Get-BuildkitLatestVersion } $Version = $Version.TrimStart('v') Write-Output "Downloading and installing Buildkit v$Version at $InstallPath" # Download files $downloadParams = @{ ToolName = "Buildkit" Repository = "$BUILDKIT_REPO" Version = $Version OSArchitecture = $OSArchitecture DownloadPath = $DownloadPath ChecksumSchemaFile = "$ModuleParentPath\Private\schemas\in-toto.sbom.schema.json" FileFilterRegEx = $null } $downloadParamsProperties = [FileDownloadParameters]::new( $downloadParams.ToolName, $downloadParams.Repository, $downloadParams.Version, $downloadParams.OSArchitecture, $downloadParams.DownloadPath, $downloadParams.ChecksumSchemaFile, $downloadParams.FileFilterRegEx ) $sourceFile = Get-InstallationFile -FileParameters $downloadParamsProperties # Untar downloaded file at install path $params = @{ Feature = "Buildkit" InstallPath = $InstallPath SourceFile = "$sourceFile" EnvPath = "$InstallPath\bin" cleanup = $true } Install-RequiredFeature @params Write-Output "Successfully installed Buildkit v$Version at $InstallPath`n" # Register Buildkitd service $showCommands = $true try { if ($Setup) { Register-BuildkitdService -BuildKitPath $InstallPath -WinCNIPath $WinCNIPath -Start -Force:$true $showCommands = $false } } catch { Write-Warning "Failed to registed and start Buildkitd service. $_" } if ($showCommands) { $commands = (Get-command -Name '*buildkit*' | Where-Object { $_.Source -like 'Containers-Toolkit' -and $_.Name -ne 'Install-Buildkit' }).Name $message = "Other useful Buildkit commands: $($commands -join ', ').`nTo learn more about each command, run Get-Help <command-name>, e.g., 'Get-Help `"*buildkit*`"' or 'Get-Help Register-BuildkitdService'`n" Write-Information -MessageData $message -Tags "Instructions" -InformationAction Continue } # Show buildkit binaries help Get-ChildItem -Path "C:\Program Files\buildkit\bin" | ForEach-Object { $executable = $_.Name # Remove extension from executable $commandName = $executable -replace ".exe", "" $message = "For $commandName usage: run `"$executable -h`"" Write-Information -MessageData "$message`n" -Tags "Instructions" -InformationAction Continue } } else { # Code that should be processed if doing a WhatIf operation # Must NOT change anything outside of the function / script return } } } # YAGNI: Is this necessary? function Build-BuildkitFromSource { Throw "Method or operation not implemented." } function Start-BuildkitdService { [CmdletBinding( SupportsShouldProcess = $true )] param() process { if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, "Buildkitd service will be started")) { Invoke-ServiceAction -Service 'Buildkitd' -Action 'Start' } else { # Code that should be processed if doing a WhatIf operation # Must NOT change anything outside of the function / script return } } } function Stop-BuildkitdService { [CmdletBinding( SupportsShouldProcess = $true )] param() process { if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, "Buildkitd service will be stopped")) { Invoke-ServiceAction -Service 'Buildkitd' -Action 'Stop' } else { # Code that should be processed if doing a WhatIf operation # Must NOT change anything outside of the function / script return } } } function Register-BuildkitdService { [CmdletBinding( SupportsShouldProcess = $true )] param( [parameter(HelpMessage = "Windows CNI plugin path")] [String]$WinCNIPath, [parameter(HelpMessage = "Buildkit path")] [String]$BuildKitPath, [parameter(HelpMessage = "Specify to start Buildkitd service after registration is complete")] [Switch]$Start, [parameter(HelpMessage = "Bypass confirmation to register buildkitd service")] [Switch]$Force ) begin { if (!$BuildKitPath) { $BuildKitPath = Get-DefaultInstallPath -Tool "Buildkit" } $WhatIfMessage = 'Registers buildkitd service.' if ($Start) { $WhatIfMessage = "Registers and starts buildkitd service." } } process { if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, $WhatIfMessage)) { if (Test-EmptyDirectory -Path $BuildKitPath) { Throw "Buildkit does not exist at $BuildKitPath or the directory is empty" } # If buildkitd is not installed, terminate execution if (!(Test-BuildkitdServiceExists -BuildkitPath $BuildkitPath)) { Write-Error "Buildkitd executable not installed." return } # Check buildkitd service is already registered if (Test-ServiceRegistered -Service 'Buildkitd') { Write-Warning ( -join @("buildkitd service already registered. To re-register the service, " "stop the service by running 'Stop-Service buildkitd' or 'Stop-BuildkitdService', then " "run 'buildkitd --unregister-service'. Wait for buildkitd service to be deregistered, " "then re-reun this command.")) return } if (!$force) { if (!$ENV:PESTER) { if (-not ($PSCmdlet.ShouldContinue('', "Are you sure you want to register buildkitd service?"))) { Write-Error "buildkitd service registration cancelled." return } } } Write-Output "Configuring buildkitd service" $buildkitdExecutable = "$BuildKitPath\bin\buildkitd.exe" Add-MpPreference -ExclusionProcess $buildkitdExecutable if (!$WinCNIPath) { $containerdPath = Get-DefaultInstallPath -Tool "containerd" $WinCNIPath = "$containerdPath\cni" } $cniBinDir = "$WinCNIPath\bin" $cniConfPath = "$WinCNIPath\conf\0-containerd-nat.conf" # Register buildkit service $command = "buildkitd.exe --register-service --debug --containerd-worker=true --containerd-cni-config-path=`"$cniConfPath`" --containerd-cni-binary-dir=`"$cniBinDir`" --service-name buildkitd" if (Test-ConfFileEmpty -Path $cniConfPath) { $consent = $force if (!$force) { $consent = [ActionConsent](Get-ConsentToRegisterBuildkit -Path $cniConfPath) -eq [ActionConsent]::Yes } if ($consent) { Write-Warning "Containerd conf file not found at $cniConfPath. Buildkit service will be registered without Containerd cni configurations." $command = "buildkitd.exe --register-service --debug --containerd-worker=true --service-name buildkitd" } else { Write-Error "Failed to register buildkit service. Containerd conf file not found at $cniConfPath.`n`t1. Ensure that the required CNI plugins are installed or you can install them using 'Install-WinCNIPlugin'.`n`t2. Create the file to resolve this issue .`n`t3. Rerun this command 'Register-BuildkitdService'" Throw "Failed to register buildkit service. Containerd conf file not found at $cniConfPath." } } $arguments = ($command -split " " | Select-Object -Skip 1) -join " " $output = Invoke-ExecutableCommand -Executable $buildkitdExecutable -Arguments $arguments if ($output.ExitCode -ne 0) { Throw "Failed to register buildkitd service. $($output.StandardError.ReadToEnd())" } $buildkitdService = Get-Service buildkitd -ErrorAction SilentlyContinue if ($null -eq $buildkitdService ) { Throw "Failed to register buildkitd service. $($Error[0].Exception.Message)" } Set-Service buildkitd -StartupType Automatic Write-Output "Successfully registered Buildkitd service." $output = Invoke-ExecutableCommand -Executable 'sc.exe' -arguments 'config buildkitd depend=containerd' if ($output.ExitCode -ne 0) { Write-Error "Failed to set dependency for buildkitd on containerd. $($output.StandardOutput.ReadToEnd())" } if ($Start) { Start-BuildkitdService Write-Output "Successfully started Buildkitd service." } else { Write-Information -InformationAction Continue -MessageData "To start buildkitd service, run 'Start-Service buildkitd' or 'Start-BuildkitdService'" } Write-Debug $(Get-Service 'buildkitd' -ErrorAction SilentlyContinue | Format-Table -AutoSize | Out-String) # YAGNI: Do we need a buildkitd.toml on Windows? } else { # Code that should be processed if doing a WhatIf operation # Must NOT change anything outside of the function / script return } } } function Uninstall-Buildkit { [CmdletBinding( SupportsShouldProcess = $true, ConfirmImpact = 'High' )] param( [parameter(HelpMessage = "BuildKit path")] [String]$Path, [parameter(HelpMessage = "Bypass confirmation to uninstall BuildKit")] [Switch] $Force ) begin { $tool = 'Buildkit' if (!$Path) { $Path = Get-DefaultInstallPath -Tool $tool } $WhatIfMessage = "Buildkit will be uninstalled from $path and buildkitd service will be stopped and unregistered" } process { if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, $WhatIfMessage)) { if (Test-EmptyDirectory -Path $path) { Write-Output "$tool does not exist at $Path or the directory is empty" return } $consent = $force if (!$ENV:PESTER) { $consent = $force -or $PSCmdlet.ShouldContinue($env:COMPUTERNAME, "Are you sure you want to uninstall Buildkit from $path?") } if (!$consent) { Throw "$tool uninstallation cancelled." } Write-Warning "Uninstalling preinstalled $tool at the path $path" try { Uninstall-BuildkitHelper -Path $path } catch { Throw "Could not uninstall $tool. $_" } } else { # Code that should be processed if doing a WhatIf operation # Must NOT change anything outside of the function / script return } } } function Uninstall-BuildkitHelper { param( [parameter(Mandatory = $true, HelpMessage = "Buildkit path")] [String]$Path ) if (Test-EmptyDirectory -Path $Path) { Write-Error "Buildkit does not exist at $Path or the directory is empty." return } try { if (Test-ServiceRegistered -Service 'Buildkitd') { Stop-BuildkitdService Unregister-Buildkitd -BuildkitPath $Path } } catch { Throw "Could not stop or unregister buildkitd service. $_" } # Delete the buildkit key Remove-Item -Path "HKLM:\SYSTEM\CurrentControlSet\Services\buildkit" -Recurse -Force -ErrorAction Ignore # Remove the folder where buildkit is installed and related folders Remove-Item -Path $Path -Recurse -Force # Delete Buildkit programdata Uninstall-ProgramFiles "$ENV:ProgramData\Buildkit" # Remove from env path Remove-FeatureFromPath -Feature "buildkit" Write-Output "Successfully uninstalled buildkit." } function Get-ConsentToRegisterBuildkit ($path) { $retry = 2 $consent = [ActionConsent]::No do { $title = "Buildkit conf file not found at $path." $question = "Do you want to register buildkit service without containerd cni configuration?" $choices = '&Yes', '&No' $consent = (Get-Host).UI.PromptForChoice($title, $question, $choices, 0) $retry -- } while (($retry -gt 0 ) -and ($consent -eq [ActionConsent]::No)) return $consent } function Test-BuildkitdServiceExists($buildkitPath) { $cmdRes = Get-Command -Name "buildkitd.exe" -ErrorAction Ignore $pathExists = Test-Path -Path "$BuildkitPath\bin\buildkitd.exe" return ($cmdRes -or $pathExists) } function Unregister-Buildkitd($buildkitPath) { if (!(Test-ServiceRegistered -Service 'buildkitd')) { Write-Warning "Buildkitd service does not exist as an installed service." return } # Unregister buildkit service $buildkitdExecutable = "$buildkitPath\bin\buildkitd.exe" Write-Debug "Buildkitd path: $buildkitdExecutable " $output = Invoke-ExecutableCommand -Executable $buildkitdExecutable -Arguments "--unregister-service" if ($output.ExitCode -ne 0) { Throw "Could not unregister buildkitd service. $($output.StandardError.ReadToEnd())" } else { Start-Sleep -Seconds 15 } } Export-ModuleMember -Function Get-BuildkitLatestVersion Export-ModuleMember -Function Install-Buildkit Export-ModuleMember -Function Start-BuildkitdService -Alias Start-Buildkitd Export-ModuleMember -Function Stop-BuildkitdService -Alias Stop-Buildkitd Export-ModuleMember -Function Register-BuildkitdService -Alias Register-Buildkitd Export-ModuleMember -Function Uninstall-Buildkit, Uninstall-BuildkitHelper # SIG # Begin signature block # MIIoUwYJKoZIhvcNAQcCoIIoRDCCKEACAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCApED/MQnixVGef # ipL2d8C3c3yhi0KaOOiWM5+SNrjTmqCCDZowggYYMIIEAKADAgECAhMzAAAD87lq # ZLvv9+1jAAAAAAPzMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjQwNzE3MjEwMjM0WhcNMjUwOTE1MjEwMjM0WjCBiDEL # MAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1v # bmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWlj # cm9zb2Z0IDNyZCBQYXJ0eSBBcHBsaWNhdGlvbiBDb21wb25lbnQwggEiMA0GCSqG # SIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4lnJhNSR/+M4vb7CMAivTMYSYEi0EnHXS # SDat/9BwPD2i0F1hj7W80W8dEulv51fz1NAILKbeIp2JaS777mffYHfxMUw/BcQs # yzW6J1e5xUPNguGU2GFXrc5s+jyK39AIpE6ATZecjMDuAu4P1nVEcZKDR0fBbP7Q # 8CFe13PgnZi3n/4UXbwaElvFkrwia4wh88gJ+7lDFkZUOI1H1xxENgP9y2LlCifr # HOahAXcY+TaSyeZqv+E0jV5SkBq8zNFCeWyMbooJPPsSG+LQEfS2sO4xNrNHZMlJ # iziV79h12qxLicPeswugNdD+aBXqF0fwoovPuvSwaKT/wM8okNMxAgMBAAGjggGC # MIIBfjAfBgNVHSUEGDAWBgorBgEEAYI3TBEBBggrBgEFBQcDAzAdBgNVHQ4EFgQU # 1Rdsgw/dRz6oCuO8sKNsrodmEw8wVAYDVR0RBE0wS6RJMEcxLTArBgNVBAsTJE1p # Y3Jvc29mdCBJcmVsYW5kIE9wZXJhdGlvbnMgTGltaXRlZDEWMBQGA1UEBRMNMjMx # NTIyKzUwMjUyMTAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzcitW2oynUClTBUBgNV # HR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2Ny # bC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEGCCsGAQUFBwEBBFUw # UzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9j # ZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0MAwGA1UdEwEB/wQC # MAAwDQYJKoZIhvcNAQELBQADggIBAGNFKGiwTGNU85l+83tJhB6N2BPvaT9TAkuE # 7uUniu1VU6b1/erckmXiuVqBLg2sGzE7O122bK871fNWpiEr9/pMflah1PL/+0D6 # XCodWS41UmWILRUhSqKURZ+iceq5n4EUiKcapBKhZ6uCKXSX62CVHlvNv4N6JA7N # UztE0h3jAEQeLoGSFlfNqvkTOobE5NwlMRSJWhQ6SYnswiTY1QU06pSTaP/eVedL # Rk6q3t/U9ibvVwHT+eTw1ASBbZoZOxWA3FjrK9XisOT/GoyV2w9D3gzKFGOElCFp # RvceGeRKy2N3YSRjjgV9VYlUM5G/cz4ZigACtnd4jfE7ebbKmiDv2UB1IUpxSzS4 # TAO1bkANbwJ/frfSikUPGfZS2fRRXMWZTeQL5bTBcmW7INNTIxM2MxF6QqlLtmkJ # Z3iv9cocwQ1MzWrrd1BU/ipLwgGtbw7mCestXZjP2amFeQjucAtKt9PkENnWfUex # rwQZnZJWF3mCVvJTh6ACU0CJyO0a2a+RiuxsvR8QpMw1gE3744SbM75nABu84/zd # kxfRkiXro5IsUJ+eNVXVKElvFngBzpbYtTVf1Pdcs8y0sEo6FxGQkzwKNLHe28tG # 1H3NTHUNa0LQUKRjMq/58Iav9qq4rW1QIzfXAEnBDSBFv1E4vLmx2LghqMRPyTAB # ZurrCCn1MIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkqhkiG9w0BAQsFADCB # iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl # ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMp # TWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTEwHhcNMTEw # NzA4MjA1OTA5WhcNMjYwNzA4MjEwOTA5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UE # CBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9z # b2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5n # IFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq/D6chAc # Lq3YbqqCEE00uvK2WCGfQhsqa+laUKq4BjgaBEm6f8MMHt03a8YS2AvwOMKZBrDI # OdUBFDFC04kNeWSHfpRgJGyvnkmc6Whe0t+bU7IKLMOv2akrrnoJr9eWWcpgGgXp # ZnboMlImEi/nqwhQz7NEt13YxC4Ddato88tt8zpcoRb0RrrgOGSsbmQ1eKagYw8t # 00CT+OPeBw3VXHmlSSnnDb6gE3e+lD3v++MrWhAfTVYoonpy4BI6t0le2O3tQ5GD # 2Xuye4Yb2T6xjF3oiU+EGvKhL1nkkDstrjNYxbc+/jLTswM9sbKvkjh+0p2ALPVO # VpEhNSXDOW5kf1O6nA+tGSOEy/S6A4aN91/w0FK/jJSHvMAhdCVfGCi2zCcoOCWY # OUo2z3yxkq4cI6epZuxhH2rhKEmdX4jiJV3TIUs+UsS1Vz8kA/DRelsv1SPjcF0P # UUZ3s/gA4bysAoJf28AVs70b1FVL5zmhD+kjSbwYuER8ReTBw3J64HLnJN+/RpnF # 78IcV9uDjexNSTCnq47f7Fufr/zdsGbiwZeBe+3W7UvnSSmnEyimp31ngOaKYnhf # si+E11ecXL93KCjx7W3DKI8sj0A3T8HhhUSJxAlMxdSlQy90lfdu+HggWCwTXWCV # mj5PM4TasIgX3p5O9JawvEagbJjS4NaIjAsCAwEAAaOCAe0wggHpMBAGCSsGAQQB # gjcVAQQDAgEAMB0GA1UdDgQWBBRIbmTlUAXTgqoXNzcitW2oynUClTAZBgkrBgEE # AYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB # /zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBaBgNVHR8EUzBRME+g # TaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9N # aWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsGAQUFBwEBBFIwUDBO # BggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9N # aWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MIGfBgNVHSAEgZcwgZQwgZEG # CSsGAQQBgjcuAzCBgzA/BggrBgEFBQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQu # Y29tL3BraW9wcy9kb2NzL3ByaW1hcnljcHMuaHRtMEAGCCsGAQUFBwICMDQeMiAd # AEwAZQBnAGEAbABfAHAAbwBsAGkAYwB5AF8AcwB0AGEAdABlAG0AZQBuAHQALiAd # MA0GCSqGSIb3DQEBCwUAA4ICAQBn8oalmOBUeRou09h0ZyKbC5YR4WOSmUKWfdJ5 # DJDBZV8uLD74w3LRbYP+vj/oCso7v0epo/Np22O/IjWll11lhJB9i0ZQVdgMknzS # Gksc8zxCi1LQsP1r4z4HLimb5j0bpdS1HXeUOeLpZMlEPXh6I/MTfaaQdION9Msm # AkYqwooQu6SpBQyb7Wj6aC6VoCo/KmtYSWMfCWluWpiW5IP0wI/zRive/DvQvTXv # biWu5a8n7dDd8w6vmSiXmE0OPQvyCInWH8MyGOLwxS3OW560STkKxgrCxq2u5bLZ # 2xWIUUVYODJxJxp/sfQn+N4sOiBpmLJZiWhub6e3dMNABQamASooPoI/E01mC8Cz # TfXhj38cbxV9Rad25UAqZaPDXVJihsMdYzaXht/a8/jyFqGaJ+HNpZfQ7l1jQeNb # B5yHPgZ3BtEGsXUfFL5hYbXw3MYbBL7fQccOKO7eZS/sl/ahXJbYANahRr1Z85el # CUtIEJmAH9AAKcWxm6U/RXceNcbSoqKfenoi+kiVH6v7RyOA9Z74v2u3S5fi63V4 # GuzqN5l5GEv/1rMjaHXmr/r8i+sLgOppO6/8MO0ETI7f33VtY5E90Z1WTk+/gFci # oXgRMiF670EKsT/7qMykXcGhiJtXcVZOSEXAQsmbdlsKgEhr/Xmfwb1tbWrJUnMT # DXpQzTGCGg8wghoLAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo # aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y # cG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIw # MTECEzMAAAPzuWpku+/37WMAAAAAA/MwDQYJYIZIAWUDBAIBBQCggbAwGQYJKoZI # hvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcC # ARUwLwYJKoZIhvcNAQkEMSIEIDTeXlEyd2TxztDOFN8hxRBLoLuU/FunU7hoDEMH # /AoPMEQGCisGAQQBgjcCAQwxNjA0oBSAEgBNAGkAYwByAG8AcwBvAGYAdKEcgBpo # dHRwczovL3d3dy5taWNyb3NvZnQuY29tIDANBgkqhkiG9w0BAQEFAASCAQBLUpJ+ # 45E316lveu7r9knK68rr4xWlDv0WC3MR+AZ8olYIrU4H1cCm8kxPa4M6DxygSyU1 # 8aiDBgLJ525eLp/PMAgj2QUXZLkEtsQxvUUycswm1LrzXu83GTOHhwN1kPmiTixU # fnbBwnVT8gH/IgPsuLper+w61rH0MaQmpML2iF9wbLngE5rKU6NqyyDcntujVNZ+ # aHce2tcwttGXr5eCbFRGQfYWkXd4QNSwshBNcuB2yKxyXedcQqgpXS/gzkpo4wiW # zKp4ZtUTzZjFVMLchzjS5TZDm+ElujT6cQ04XT268mML+GY3uPjLmEuw8Sohi6aM # B751wdLJy4FNOpHkoYIXlzCCF5MGCisGAQQBgjcDAwExgheDMIIXfwYJKoZIhvcN # AQcCoIIXcDCCF2wCAQMxDzANBglghkgBZQMEAgEFADCCAVIGCyqGSIb3DQEJEAEE # oIIBQQSCAT0wggE5AgEBBgorBgEEAYRZCgMBMDEwDQYJYIZIAWUDBAIBBQAEIG/W # pfaBPsImdN2jvYsh9zbz1xAZT0kt5/qk4HACALKWAgZoJdLn2ksYEzIwMjUwNTE1 # MTYyMDUwLjc0NlowBIACAfSggdGkgc4wgcsxCzAJBgNVBAYTAlVTMRMwEQYDVQQI # EwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3Nv # ZnQgQ29ycG9yYXRpb24xJTAjBgNVBAsTHE1pY3Jvc29mdCBBbWVyaWNhIE9wZXJh # dGlvbnMxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVTTjpBNDAwLTA1RTAtRDk0NzEl # MCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZaCCEe0wggcgMIIF # CKADAgECAhMzAAACAnlQdCEUfbihAAEAAAICMA0GCSqGSIb3DQEBCwUAMHwxCzAJ # BgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25k # MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jv # c29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMB4XDTI1MDEzMDE5NDI0NFoXDTI2MDQy # MjE5NDI0NFowgcsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw # DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x # JTAjBgNVBAsTHE1pY3Jvc29mdCBBbWVyaWNhIE9wZXJhdGlvbnMxJzAlBgNVBAsT # Hm5TaGllbGQgVFNTIEVTTjpBNDAwLTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9z # b2Z0IFRpbWUtU3RhbXAgU2VydmljZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC # AgoCggIBALd5Knpy5xQY6Rw+Di8pYol8RB6yErZkGxhTW0Na9C7ov2Wn52eqtqMh # 014fUc3ejPeKIagla43YdU1mRw63fxpYZ5szSBRQ60+O4uG47l3rtilCwcEkBaFy # 978xV2hA+PWeOICNKI6svzEVqsUsjjpEfw14OEA9dwmlafsAjMLIiNk5onYNYD7p # DA3PCqMGAil/WFYXCoe88R53LSei1du1Z9P28JIv2x0Mror8cf0expjnAuZRQHtJ # +4sajU5YSbownIbaOLGqL03JGjKl0Xx1HKNbEpGXYnHC9t62UNOKjrpeWJM5ySrZ # GAz5mhxkRvoSg5213RcqHcvPHb0CEfGWT7p4jBq+Udi44tkMqh085U3qPUgn1uui # VjqZluhDnU6p7mcQzmH9YlfbwYtmKgSQk3yo57k/k/ZjH0eg6ou6BfTSoLPGrgEO # bzEfzkcrG8oI7kqKSilpEYa1CVeMPK6wxaWsdzJK3noOEvh1xWeft0W8vnTO9CUV # kyFWh6FZJCSRa5SUIKog6tN7tFuadt0miwf7uUL6fneCcrLg6hnO5R6rMKdIHUk1 # c8qcmiM/cN7nHCymLm1S9AU1+V8ZOyNmBACAMF2D8M7RMaAtEMq9lAJnmoi5elBH # KDfvJznV73nPxTabKxTRedKlZ6KAeqTI4C0N9wimrka/sdX51rZHAgMBAAGjggFJ # MIIBRTAdBgNVHQ4EFgQU2ga5tQ+M/Z/yJ+Qgq/DLWuVIdNkwHwYDVR0jBBgwFoAU # n6cVXQBeYl2D9OXSZacbUzUZ6XIwXwYDVR0fBFgwVjBUoFKgUIZOaHR0cDovL3d3 # dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWljcm9zb2Z0JTIwVGltZS1TdGFt # cCUyMFBDQSUyMDIwMTAoMSkuY3JsMGwGCCsGAQUFBwEBBGAwXjBcBggrBgEFBQcw # AoZQaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNyb3Nv # ZnQlMjBUaW1lLVN0YW1wJTIwUENBJTIwMjAxMCgxKS5jcnQwDAYDVR0TAQH/BAIw # ADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAOBgNVHQ8BAf8EBAMCB4AwDQYJKoZI # hvcNAQELBQADggIBAIPzdoVBTE3fseQ6gkMzWZocVlVQZypNBw+c4PpShhEyYMq/ # QZpseUTzYBiAs+5WW6Sfse0k8XbPSOdOAB9EyfbokUs8bs79dsorbmGsE8nfSUG7 # CMBNW3nxQDUFajuWyafKu6v/qHwAXOtfKte2W/NBippFhj2TRQVjkYz6f1hoQQrY # Pbrx75r4cOZZ761gvYf707hDUxAtqD5yI3AuSP/5CXGleJai70q8A/S0iT58fwXf # DDlU5OL1pn36o+OzPDfUfid22K8FlofmzlugmYfYlu0y5/bLuFJ0l0TRRbYHQURk # 8siZ6aUqGyUk1WoQ7tE+CXtzzVC5VI7nx9+mZvC1LGFisRLdWw+CVef04MXsOqY8 # wb8bKwHij9CSk1Sr7BLts5FM3Oocy0f6it3ZhKZr7VvJYGv+LMgqCA4J0TNpkN/K # bXYYzprhL4jLoBQinv8oikCZ9Z9etwwrtXsQHPGh7OQtEQRYjhe0/CkQGe05rWgM # fdn/51HGzEvS+DJruM1+s7uiLNMCWf/ZkFgH2KhR6huPkAYvjmbaZwpKTscTnNRF # 5WQgulgoFDn5f/yMU7X+lnKrNB4jX+gn9EuiJzVKJ4td8RP0RZkgGNkxnzjqYNun # XKcr1Rs2IKNLCZMXnT1if0zjtVCzGy/WiVC7nWtVUeRI2b6tOsvArW2+G/SZMIIH # cTCCBVmgAwIBAgITMwAAABXF52ueAptJmQAAAAAAFTANBgkqhkiG9w0BAQsFADCB # iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl # ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMp # TWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMjEw # OTMwMTgyMjI1WhcNMzAwOTMwMTgzMjI1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UE # CBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9z # b2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQ # Q0EgMjAxMDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAOThpkzntHIh # C3miy9ckeb0O1YLT/e6cBwfSqWxOdcjKNVf2AX9sSuDivbk+F2Az/1xPx2b3lVNx # WuJ+Slr+uDZnhUYjDLWNE893MsAQGOhgfWpSg0S3po5GawcU88V29YZQ3MFEyHFc # UTE3oAo4bo3t1w/YJlN8OWECesSq/XJprx2rrPY2vjUmZNqYO7oaezOtgFt+jBAc # nVL+tuhiJdxqD89d9P6OU8/W7IVWTe/dvI2k45GPsjksUZzpcGkNyjYtcI4xyDUo # veO0hyTD4MmPfrVUj9z6BVWYbWg7mka97aSueik3rMvrg0XnRm7KMtXAhjBcTyzi # YrLNueKNiOSWrAFKu75xqRdbZ2De+JKRHh09/SDPc31BmkZ1zcRfNN0Sidb9pSB9 # fvzZnkXftnIv231fgLrbqn427DZM9ituqBJR6L8FA6PRc6ZNN3SUHDSCD/AQ8rdH # GO2n6Jl8P0zbr17C89XYcz1DTsEzOUyOArxCaC4Q6oRRRuLRvWoYWmEBc8pnol7X # KHYC4jMYctenIPDC+hIK12NvDMk2ZItboKaDIV1fMHSRlJTYuVD5C4lh8zYGNRiE # R9vcG9H9stQcxWv2XFJRXRLbJbqvUAV6bMURHXLvjflSxIUXk8A8FdsaN8cIFRg/ # eKtFtvUeh17aj54WcmnGrnu3tz5q4i6tAgMBAAGjggHdMIIB2TASBgkrBgEEAYI3 # FQEEBQIDAQABMCMGCSsGAQQBgjcVAgQWBBQqp1L+ZMSavoKRPEY1Kc8Q/y8E7jAd # BgNVHQ4EFgQUn6cVXQBeYl2D9OXSZacbUzUZ6XIwXAYDVR0gBFUwUzBRBgwrBgEE # AYI3TIN9AQEwQTA/BggrBgEFBQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29t # L3BraW9wcy9Eb2NzL1JlcG9zaXRvcnkuaHRtMBMGA1UdJQQMMAoGCCsGAQUFBwMI # MBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjAPBgNVHRMB # Af8EBTADAQH/MB8GA1UdIwQYMBaAFNX2VsuP6KJcYmjRPZSQW9fOmhjEMFYGA1Ud # HwRPME0wS6BJoEeGRWh0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3By # b2R1Y3RzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNybDBaBggrBgEFBQcBAQRO # MEwwSgYIKwYBBQUHMAKGPmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2Vy # dHMvTWljUm9vQ2VyQXV0XzIwMTAtMDYtMjMuY3J0MA0GCSqGSIb3DQEBCwUAA4IC # AQCdVX38Kq3hLB9nATEkW+Geckv8qW/qXBS2Pk5HZHixBpOXPTEztTnXwnE2P9pk # bHzQdTltuw8x5MKP+2zRoZQYIu7pZmc6U03dmLq2HnjYNi6cqYJWAAOwBb6J6Gng # ugnue99qb74py27YP0h1AdkY3m2CDPVtI1TkeFN1JFe53Z/zjj3G82jfZfakVqr3 # lbYoVSfQJL1AoL8ZthISEV09J+BAljis9/kpicO8F7BUhUKz/AyeixmJ5/ALaoHC # gRlCGVJ1ijbCHcNhcy4sa3tuPywJeBTpkbKpW99Jo3QMvOyRgNI95ko+ZjtPu4b6 # MhrZlvSP9pEB9s7GdP32THJvEKt1MMU0sHrYUP4KWN1APMdUbZ1jdEgssU5HLcEU # BHG/ZPkkvnNtyo4JvbMBV0lUZNlz138eW0QBjloZkWsNn6Qo3GcZKCS6OEuabvsh # VGtqRRFHqfG3rsjoiV5PndLQTHa1V1QJsWkBRH58oWFsc/4Ku+xBZj1p/cvBQUl+ # fpO+y/g75LcVv7TOPqUxUYS8vwLBgqJ7Fx0ViY1w/ue10CgaiQuPNtq6TPmb/wrp # NPgkNWcr4A245oyZ1uEi6vAnQj0llOZ0dFtq0Z4+7X6gMTN9vMvpe784cETRkPHI # qzqKOghif9lwY1NNje6CbaUFEMFxBmoQtB1VM1izoXBm8qGCA1AwggI4AgEBMIH5 # oYHRpIHOMIHLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G # A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUw # IwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5u # U2hpZWxkIFRTUyBFU046QTQwMC0wNUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29m # dCBUaW1lLVN0YW1wIFNlcnZpY2WiIwoBATAHBgUrDgMCGgMVAEmJSGkJYD/df+Nn # IjLTJ7pEnAvOoIGDMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp # bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw # b3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAw # DQYJKoZIhvcNAQELBQACBQDr0FFkMCIYDzIwMjUwNTE1MTE0MTI0WhgPMjAyNTA1 # MTYxMTQxMjRaMHcwPQYKKwYBBAGEWQoEATEvMC0wCgIFAOvQUWQCAQAwCgIBAAIC # AIwCAf8wBwIBAAICEfYwCgIFAOvRouQCAQAwNgYKKwYBBAGEWQoEAjEoMCYwDAYK # KwYBBAGEWQoDAqAKMAgCAQACAwehIKEKMAgCAQACAwGGoDANBgkqhkiG9w0BAQsF # AAOCAQEAqqUjDKJUQO5S5oWMRobJDrdADBYfc7j4mRkYrEUPK4jrYzGKExolvKc4 # XPg4FTbNNC26hewAakPwYzv/W7lVUvT9bC/SDlDbHIBlJ8fn5W9xERKAGJW/RsAO # 419HsXOWuRUHfPDKxRXk3keFzUKCWZ/W5lvKPbcuWsOBEgeRopyjtt5WL8HJrI7u # tXSEMRg5EF4rza6a+mmK5o93iGgHcfSdYw6JxD6/BIwyu7Ym0wGrlfTGjC7b3lVv # nD2ToFSmLdZdpJtg4AtHPF/IEvV0O8gIFtuncGwmu844MkSqa17rc80SsUDnYDOO # dyhkBJwAW26ikitsppQr3ny6MonygTGCBA0wggQJAgEBMIGTMHwxCzAJBgNVBAYT # AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD # VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBU # aW1lLVN0YW1wIFBDQSAyMDEwAhMzAAACAnlQdCEUfbihAAEAAAICMA0GCWCGSAFl # AwQCAQUAoIIBSjAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwLwYJKoZIhvcN # AQkEMSIEIGYkizWd//3VNCxouFnmTFsUxYITjgZR4xoP9klZeueqMIH6BgsqhkiG # 9w0BCRACLzGB6jCB5zCB5DCBvQQg843qARgHlsvNcta5SYvxl3zFcCypeSx50XKi # V8yUX+wwgZgwgYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv # bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0 # aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAA # AgJ5UHQhFH24oQABAAACAjAiBCCneRkQYbjGMp3Wxu2U5Dh+xgPqhLWg/GvX2xjk # cWuAyzANBgkqhkiG9w0BAQsFAASCAgCJ7NkjgdemzYbnc2FnR3/IDNqekPJrYCnN # VomF+kRMbtUloC8GBzCMDqpWvVNiqIP/QMWZdZw1tVrR9sH648e/+jzA6Ck1154r # MGXGanEF+SPdDGF029ZklBem+01NMwYRGcXOLnyO67UUev+FI5OEzaKTUxKrIPop # YjKfC15XjllXVXVo+xIkkQvEmfIc5TQcMRr9VbaNUTCON+cXRt3N2/q7sndgKovj # +pKTa5oBMpl8yzl0SjhPh6bGxZJZtVomddQfUZB1cMcjp94n9NDXJLrI9wUOGnRj # mQ33fPUy4wicULKm4NneNkmpr80GkGJqxrDbtqfp5gqsoooc015swbSA/7l8yb7b # W4zOHr76Jd6Q4dJn3Y6Sicm21v342wlz84RsS7dOPwE8tPY2WItivgXzRShExX8s # o2OiwdnhImIFLGwlSm5lso7B1p85a7bhpgvDVnTZcOR70kp6Ip4ETXbcuk6Ldd7r # el+Mbjyrb+KPqWGDRRXS0FOleAVIludUP7mMWX0k7lkajkxiu8XP3ye7wk6Xj8nq # tKuFcn3t7IgW3LRWv1UVLubI5toLlluJ+p9YYEx1TyIN5ZcQt5i5p/98jwR/1YGG # YZmuh9aDV55t7AkENqX4hCreZzSJC+gvPq8flMD24xLChrQ3LaW7ALIjYn9tS3Ks # 3CpDHxZGCw== # SIG # End signature block |