Public/AllToolsUtilities.psm1
########################################################################### # # # Copyright (c) Microsoft Corporation. All rights reserved. # # # # This code is licensed under the MIT License (MIT). # # # ########################################################################### $ModuleParentPath = Split-Path -Parent $PSScriptRoot Import-Module -Name "$ModuleParentPath\Private\CommonToolUtilities.psm1" -Force function Show-ContainerTools { param ( [Parameter(HelpMessage = "Show latest release version")] [Switch]$Latest, [Parameter(HelpMessage = "Tool to show")] [ValidateSet("containerd", "buildkit", "nerdctl")] [String[]]$ToolName ) $tools = if ($ToolName) { $ToolName } else { @("containerd", "buildkit", "nerdctl") } $installedTools = @() foreach ($tool in $tools) { $installedTools += (Get-InstalledVersion -Feature $tool -Latest:$Latest) } $registerCommands = (Get-Command -Name "*-*Service" -Module 'Containers-Toolkit').Name -join ', ' $message = "For unregistered services/daemons, check the tool's help or register the service using `n`t$registerCommands" Write-Information -MessageData $message -Tags "Instructions" -InformationAction Continue return $installedTools } function Install-ContainerTools { [CmdletBinding( SupportsShouldProcess = $true, ConfirmImpact = 'High' )] param( [string] [ValidateNotNullOrEmpty()] [parameter(HelpMessage = "ContainerD version to use")] $ContainerDVersion = "latest", [string] [ValidateNotNullOrEmpty()] [parameter(HelpMessage = "Buildkit version to use")] $BuildKitVersion = "latest", [string] [ValidateNotNullOrEmpty()] [parameter(HelpMessage = "nerdctl version to use")] $NerdCTLVersion = "latest", [String] [parameter(HelpMessage = "Path to Install files. Defaults to Program Files")] $InstallPath = $Env:ProgramFiles, [String] [parameter(HelpMessage = "Path to download files. Defaults to user's Downloads folder")] $DownloadPath = "$HOME\Downloads", [Switch] [parameter(HelpMessage = "Force install the tools even if they already exists at the specified path")] $Force, [switch] [parameter(HelpMessage = "Register and Start Containerd and Buildkitd services and set up NAT network")] $RegisterServices ) begin { # Strip leading "v" from version $containerdVersion = $containerdVersion.TrimStart("v") $buildKitVersion = $buildKitVersion.TrimStart("v") $nerdctlVersion = $nerdctlVersion.TrimStart("v") $toInstall = @("containerd v$containerdVersion", "buildkit v$buildKitVersion", "nerdctl v$nerdctlVersion") $toInstallString = $($toInstall -join ', ') $WhatIfMessage = "$toInstallString will be installed. Any downloaded files will be removed" if ($Force) { $WhatIfMessage = "$toInstallString will be automatically uninstalled (if they are already installed) and reinstalled. Any downloaded files will be removed" } } process { if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, $WhatIfMessage)) { Write-Output "The following tools will be installed: $toInstallString" Write-Debug "Downloading files to $DownloadPath" Write-Debug "Installing files to $InstallPath" $completedInstalls = @() $failedInstalls = @() $installTasks = @( @{ name = "Containerd" function = { Install-Containerd -Force:$force -Confirm:$false ` -Version $containerdVersion ` -InstallPath "$InstallPath\Containerd" ` -DownloadPath "$DownloadPath" ` -Setup:$RegisterServices } }, @{ name = "Buildkit" function = { Install-Buildkit -Force:$force -Confirm:$false ` -Version $buildKitVersion ` -InstallPath "$InstallPath\Buildkit" ` -DownloadPath "$DownloadPath" ` -Setup:$RegisterServices } }, @{ name = "nerdctl" function = { Install-Nerdctl -Force:$force -Confirm:$false ` -Version $nerdctlVersion ` -InstallPath "$InstallPath\nerdctl" ` -DownloadPath "$DownloadPath" } } ) foreach ($task in $installTasks) { try { & $task.Function $completedInstalls += $task.Name } catch { Write-Error "$($task.Name) installation failed. $_" $failedInstalls += $task.Name } } if ($completedInstalls) { Write-Output "$($completedInstalls -join ', ') installed successfully.`n" } if ($failedInstalls) { Write-Warning "Installation failed for $($failedInstalls -join ', ')`n" } if ($RegisterServices) { try { Initialize-NatNetwork -Force:$force -Confirm:$false } catch { Write-Error "Failed to initialize NAT network. $_" } } else { $message = @" To register containerd and buildkitd services and create a NAT network, see help on the following commands: Get-Help Register-ContainerdService Get-Help Register-BuildkitdService Get-Help Initialize-NatNetwork "@ Write-Information -MessageData $message -Tags "Instructions" -InformationAction Continue } Write-Output "Installation complete. See logs for more details" } else { # Code that should be processed if doing a WhatIf operation # Must NOT change anything outside of the function / script return } } } function Get-InstalledVersion($feature, $Latest) { $sourceLocation = $null $daemon = $null $buildctlPath = $null switch ($feature) { "buildkit" { $blktCommandInfo = Get-Command "build*.exe" | Where-Object { $_.Source -like "*buildkit*" } if ($null -ne $blktCommandInfo) { # Get buildkitd executable $buldkitdCommandInfo = $blktCommandInfo | Where-Object { $_.Name -like "buildkitd.exe" } $sourceLocation = $buldkitdCommandInfo.Source } $daemon = 'buildkitd' $buildctlPath = ($blktCommandInfo | Where-Object { $_.Name -like "buildctl.exe" }).Source } Default { $commandInfo = Get-Command "$feature.exe" -ErrorAction Ignore if ($null -ne $commandInfo) { $sourceLocation = $commandInfo.Source } if ($feature -eq 'containerd') { $daemon = 'containerd' } } } $result = [PSCustomObject]@{ Tool = $feature Installed = $False } if ($sourceLocation) { $result = getToolVersion -Executable $sourceLocation Add-Member -InputObject $result -Name 'Tool' -Value $feature -MemberType 'NoteProperty' Add-Member -InputObject $result -Name 'Path' -Value $sourceLocation -MemberType 'NoteProperty' $result = $result | Select-Object Tool, Path, Installed, Version if ($daemon) { Add-Member -InputObject $result -Name 'Daemon' -Value $daemon -MemberType 'NoteProperty' Add-Member -InputObject $result -Name 'DaemonStatus' -MemberType 'NoteProperty' ` -Value (getDaemonStatus -Daemon $daemon) } if ($buildctlPath) { $result | Add-Member -Name 'BuildctlPath' -Value $buildctlPath -MemberType 'NoteProperty' } } # Get latest version $latestVersion = "-" if ($Latest) { $latestVersionCommand = "Get-$($feature)LatestVersion" $latestVersion = & $latestVersionCommand Add-Member -InputObject $result -Name 'LatestVersion' -Value "v$latestVersion" -MemberType 'NoteProperty' } return $result } function getToolVersion($executable) { $toolName = [System.IO.Path]::GetFileNameWithoutExtension([System.IO.Path]::GetFileName($executable)) $installedVersion = $null try { $cmdOutput = Invoke-ExecutableCommand -Executable $executable -Arguments '--version' if ($cmdOutput.ExitCode -ne 0) { Throw "Couldn't get $toolName version. $($cmdOutput.StandardError.ReadToEnd())" } $version = $cmdOutput.StandardOutput.ReadToEnd() $pattern = "(\d+\.)(\d+\.)(\*|\d+)" $installedVersion = ($version | Select-String -Pattern $pattern).Matches.Value if ($installedVersion) { $installedVersion = "v$installedVersion" } else { $installedVersion = 'unknown' } } catch { $installedVersion = "-" } $Installed = ($null -ne $installedVersion) if (!$Installed) { $executablePath = Get-Command $executable.Source -ErrorAction Ignore $installed = ($null -ne $executablePath) } $result = [PSCustomObject]@{ Installed = $Installed Version = $installedVersion } return $result } function getDaemonStatus($daemon) { $daemonStatus = Get-Service -Name $daemon -ErrorAction Ignore if ($null -eq $daemonStatus) { return 'Unregistered' } return $daemonStatus.Status } Export-ModuleMember -Function Show-ContainerTools Export-ModuleMember -Function Install-ContainerTools # SIG # Begin signature block # MIIoWQYJKoZIhvcNAQcCoIIoSjCCKEYCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBtvg+gURWCBzd3 # oEn+qNq/xnt1bdyz8tJa12MD/KP87qCCDYswggYJMIID8aADAgECAhMzAAAD9LjE # XeFOcLZ+AAAAAAP0MA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD # VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy # b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p # bmcgUENBIDIwMTEwHhcNMjQwNzE3MjEwMjM1WhcNMjUwOTE1MjEwMjM1WjCBiDEL # MAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1v # bmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWlj # cm9zb2Z0IDNyZCBQYXJ0eSBBcHBsaWNhdGlvbiBDb21wb25lbnQwggEiMA0GCSqG # SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCv3P8bL08GKolFW7QNDVOF0aM4iqMxVvAW # VM124/82xbjAraJkKxieMrQa1Fc95LVGgxmJIi5R6QKMz2MO9bnwC7kSkPqoZJil # 26bRLY6jinjbwPpK3TzbW7z9bXfWw5bPFlt72NVIdXJ3xtHoYa+AOi++CF2Ry7+7 # o1AzvotJwG6lQSiCMKeMt8apqEF1f+QkDFEUv5tezw9748DeHW9orvo4IPzWa7vW # QgljB08LKSnzTN9/Jot2coWpFv4YuEoJZmR2ofPJMnDUUruDORTXnxwhfvd/wUmI # SoEysSqobkNV+qFuUmSShYrx8R1zHm7P6G/iRMIKYmSrIYBKUvndAgMBAAGjggFz # MIIBbzAfBgNVHSUEGDAWBgorBgEEAYI3TBEBBggrBgEFBQcDAzAdBgNVHQ4EFgQU # Dz4uMjS8YCSZaU0449GJYQ1ufyowRQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1p # Y3Jvc29mdCBDb3Jwb3JhdGlvbjEWMBQGA1UEBRMNMjMxNTIyKzUwMjUxODAfBgNV # HSMEGDAWgBRIbmTlUAXTgqoXNzcitW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNo # dHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0Ey # MDExXzIwMTEtMDctMDguY3JsMGEGCCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZF # aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQ # Q0EyMDExXzIwMTEtMDctMDguY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQEL # BQADggIBABUCAiEn4g8i5T3VCP8160IY4ERdvZi5QZ2pSnBPW1dswVhLxkNTiCTV # XKDjTQ4EwDBNSZZGJePz4+t86pKhlBON3S7wswf5fCovJLlIiKbw+E4TZeY6xAxd # +5zV7Q2lsQhPHxiOY0PIGUE0KJfv/DQUulD8DrE0rru7yOO+DJI0muoK0BbHhRfd # mAJhp2gbYRkarEIkhML9m3gR12mCBb69Vocm4IyOBivUPMjjvQMkERF7cR07k2uP # 6dmpR8wtof9la0/K0wgiP5XuQUsAqgzhXrljH7dK7nqGrBDjJtrRdYfvVL+Rcz9i # YZO280g2uNtac5em3HOEsactAL7XKqZ4o7s9sRyp/bTNLLRmhFMB729IL+Hi0YM7 # C8th3HZ5nP+77L46KUGip6QgRIJs+EO0YNW+AwgMxPfKpTx/Ggh8Z85kP7HLDZJk # ZdPO/3cgVOTO4ax21vO2yMPCdfoGGr2ZLZw4SjEbGuOZJ22iGMV7tBvHk8nWAt3q # +j/icAq99GA1nIPnw3jK3K9OwGqwA9eiWsO8/bHMm6s50UKIFupMKm6qObosaVBy # R58rf8Cxumka7hPy1eSJSzQyA4UqYNTWuChsTfqgRLmLomS6yAu7t4r/bM4mGl+2 # Ki+avhQ4COm3jWWd0V6UGIP3T4zaKNs2GWFBIYsb/6XVvvi7pz/JMIIHejCCBWKg # AwIBAgIKYQ6Q0gAAAAAAAzANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMx # EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT # FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3Qg # Q2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYw # NzA4MjEwOTA5WjB+MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQ # MA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u # MSgwJgYDVQQDEx9NaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBDQSAyMDExMIICIjAN # BgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGf # Qhsqa+laUKq4BjgaBEm6f8MMHt03a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRg # JGyvnkmc6Whe0t+bU7IKLMOv2akrrnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NE # t13YxC4Ddato88tt8zpcoRb0RrrgOGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnn # Db6gE3e+lD3v++MrWhAfTVYoonpy4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+E # GvKhL1nkkDstrjNYxbc+/jLTswM9sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+t # GSOEy/S6A4aN91/w0FK/jJSHvMAhdCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxh # H2rhKEmdX4jiJV3TIUs+UsS1Vz8kA/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AV # s70b1FVL5zmhD+kjSbwYuER8ReTBw3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f # 7Fufr/zdsGbiwZeBe+3W7UvnSSmnEyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3D # KI8sj0A3T8HhhUSJxAlMxdSlQy90lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9Jaw # vEagbJjS4NaIjAsCAwEAAaOCAe0wggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1Ud # DgQWBBRIbmTlUAXTgqoXNzcitW2oynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBi # AEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRy # LToCMZBDuRQFTuHqp8cx0SOJNDBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3Js # Lm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDEx # XzIwMTFfMDNfMjIuY3JsMF4GCCsGAQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0 # cDovL3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDEx # XzIwMTFfMDNfMjIuY3J0MIGfBgNVHSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/ # BggrBgEFBQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2Nz # L3ByaW1hcnljcHMuaHRtMEAGCCsGAQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAA # bwBsAGkAYwB5AF8AcwB0AGEAdABlAG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUA # A4ICAQBn8oalmOBUeRou09h0ZyKbC5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+ # vj/oCso7v0epo/Np22O/IjWll11lhJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4H # Limb5j0bpdS1HXeUOeLpZMlEPXh6I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6 # aC6VoCo/KmtYSWMfCWluWpiW5IP0wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiX # mE0OPQvyCInWH8MyGOLwxS3OW560STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn # +N4sOiBpmLJZiWhub6e3dMNABQamASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAq # ZaPDXVJihsMdYzaXht/a8/jyFqGaJ+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5h # YbXw3MYbBL7fQccOKO7eZS/sl/ahXJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/ # RXceNcbSoqKfenoi+kiVH6v7RyOA9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXm # r/r8i+sLgOppO6/8MO0ETI7f33VtY5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMyk # XcGhiJtXcVZOSEXAQsmbdlsKgEhr/Xmfwb1tbWrJUnMTDXpQzTGCGiQwghogAgEB # MIGVMH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQH # EwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNV # BAMTH01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTECEzMAAAP0uMRd4U5w # tn4AAAAAA/QwDQYJYIZIAWUDBAIBBQCggbAwGQYJKoZIhvcNAQkDMQwGCisGAQQB # gjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkE # MSIEIKlm5XKKamc0TB8uZG0pFQRCoUZp6hsodUBJZ1F5H6byMEQGCisGAQQBgjcC # AQwxNjA0oBSAEgBNAGkAYwByAG8AcwBvAGYAdKEcgBpodHRwczovL3d3dy5taWNy # b3NvZnQuY29tIDANBgkqhkiG9w0BAQEFAASCAQCnhrbYDCzSQtOiPC4VMPJhk0XO # wn4PDcMaGBqgXcRGErkcNLrGYJepTUHIJ0zrsgJsrkd7kOZ7P3Vbhd5QXGSoznFu # OksVZimz5iFbbI+9iCkPnVOpLr5Ieo3aq5EHrcr0FIerjzBp1D6wa9yetXrJ1s2G # 3u7t7Esro7qSoRuAIxYFjRd6xhnscumWGEkATETdQ94obR/8Jp2ImGPZaX5HI0ns # aIMKNvCopJLC110IARbgSEriQBJm+sbl3jIJ/Ld2e4UIs54Z9p7I2UhRSg4i/jBV # nPyKf173Ktm4p88O+pkEv334S+XKkAHCVv4u4lvUgCTmcpj9Lv0+6bGtHcNsoYIX # rDCCF6gGCisGAQQBgjcDAwExgheYMIIXlAYJKoZIhvcNAQcCoIIXhTCCF4ECAQMx # DzANBglghkgBZQMEAgEFADCCAVkGCyqGSIb3DQEJEAEEoIIBSASCAUQwggFAAgEB # BgorBgEEAYRZCgMBMDEwDQYJYIZIAWUDBAIBBQAEIOGGyHdpgR0OlXIZSdC6yjY1 # 3zFmPZwq+n9Npyc0S4CsAgZoEseHZMMYEjIwMjUwNTE1MTYxOTUyLjI4WjAEgAIB # 9KCB2aSB1jCB0zELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAO # BgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEt # MCsGA1UECxMkTWljcm9zb2Z0IElyZWxhbmQgT3BlcmF0aW9ucyBMaW1pdGVkMScw # JQYDVQQLEx5uU2hpZWxkIFRTUyBFU046NTIxQS0wNUUwLUQ5NDcxJTAjBgNVBAMT # HE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2WgghH7MIIHKDCCBRCgAwIBAgIT # MwAAAgAL16p/GyoXVgABAAACADANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJV # UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE # ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGlt # ZS1TdGFtcCBQQ0EgMjAxMDAeFw0yNDA3MjUxODMxMjFaFw0yNTEwMjIxODMxMjFa # MIHTMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQL # EyRNaWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJzAlBgNVBAsT # Hm5TaGllbGQgVFNTIEVTTjo1MjFBLTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9z # b2Z0IFRpbWUtU3RhbXAgU2VydmljZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC # AgoCggIBAK9V2mnSpD9k5Lp6Exee9/7ReyiTPQ6Ir93HL9upqp1IZr9gzOfYpBE+ # Fp0X6OW4hSB3Oi6qyHqgoE/X0/xpLOVSjvdGUFtmr4fzzB55dJGX1/yOc3VaKFx2 # 3VFJD4mXzV7M1rMJi/VJVqPJs8r/S6fUwLcP6FzmEwMXWEqjgeVM89UNwPLgqTZb # pkDQyRg2OnEp9DJWLpF5JQKwoaupfimK5eq/1pzql0pJwAaYIErCd96C96J5g4jf # WFAKWcI5zYfTOpA2p3ks+/P2LQ/9qRqcffy1xC6GsxFBcYcoOCnZqFhjWMHUe/4n # fNYHjhEevZeXSb+9Uv5h/i8W+i+vdp/LhJgFcOn1bxPnPMI4GGW5WQjTwMpwpw3b # kS3ZNY7MAqo6jXN1/1iMwOxhrOB1EuGCKwFMfB9gPeLwzYgPAFmu2fx0sEwsiIHl # W5XV2DNgbcTCqt5J3kaE9uzUO2O5/GU2gI3uwZX47vN7KRj/0FmDWdcGM2FRkcjq # XQPFpsauVfH+a+B2hvcz3MpDsiaUWcvld0RooIRZrAiVwHDM4ju+h4p8AiIyJpwh # ShifyGy4x+ie3yV6kT24Ph+q2C2fFwaZlwRR+D02pGVWMQfz/hEGy+SzcNGSDPnr # n8QpY1eDvpx5DPs4EsfPtOwVWTwSrJaKHm7JoSHATtO+/ZHoXImDAgMBAAGjggFJ # MIIBRTAdBgNVHQ4EFgQUgCUk2r4JIyqoHucUDl59+X13dzowHwYDVR0jBBgwFoAU # n6cVXQBeYl2D9OXSZacbUzUZ6XIwXwYDVR0fBFgwVjBUoFKgUIZOaHR0cDovL3d3 # dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWljcm9zb2Z0JTIwVGltZS1TdGFt # cCUyMFBDQSUyMDIwMTAoMSkuY3JsMGwGCCsGAQUFBwEBBGAwXjBcBggrBgEFBQcw # AoZQaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNyb3Nv # ZnQlMjBUaW1lLVN0YW1wJTIwUENBJTIwMjAxMCgxKS5jcnQwDAYDVR0TAQH/BAIw # ADAWBgNVHSUBAf8EDDAKBggrBgEFBQcDCDAOBgNVHQ8BAf8EBAMCB4AwDQYJKoZI # hvcNAQELBQADggIBACjwhvZ40bSKkPn7hAoMc1jLEDiNx71u7FfT5hFggjlpU7hg # iMzYt4m3S2UtG9iAx4NMi67XVbgYtxcVXXrCF7s2MqHyHv2pUwXVeA4Yoy017Qez # YDp6Oxtdojt7eo8tYT0qrsxi68v9phGQcCLEqEtg/h/txwicTw8oczBaj/qZZbTw # Agf0DcGe6vhxsmb97/Hrfq0GIPLBdz07lng4N3Uf85NTWsCf3XxQg2JVjXggQi7z # T0AXHjGFxURSoXElMLO5hXSAw4WacasiCg9lg8BcjSBhHs5/p3eJF0bqXjRMfnkq # SV8pUQ/tXeOYW+j8ziBewZHD7UbRVtsF4JIy6rU1lpQZL85drjX2Cdwj2VWg8jA2 # ml4Dvh+g4q7CeCBvYpCHfeNfplg3o5I+WmJ/UDekTn6PxzR4NbYpsKRaFIr6gBbu # oq1mRcOVfsi6/BS3O52zGtpRUosc7ves3Zw7DyJs9HOkrW2MoSkpTN7g0YvVFsnU # iqpxG7SejJPmLsb86a5LlkCWFn6T77oPsE54qMpFcHNMkVXLHeMTM5550bWQxjEl # BJfbTFZ3m2EbIcGSMiU7AYC2ZhzO6tkxSv1/feOEpCKsmNtgHLi3tBqqDXwEgiHG # bc22f8z+JU9vzdKQ259n3wM42ZISPkK6q/fN5kGVsGXa905NTGBJQ04c9g9DMIIH # 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 # qzqKOghif9lwY1NNje6CbaUFEMFxBmoQtB1VM1izoXBm8qGCA1YwggI+AgEBMIIB # AaGB2aSB1jCB0zELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAO # BgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEt # MCsGA1UECxMkTWljcm9zb2Z0IElyZWxhbmQgT3BlcmF0aW9ucyBMaW1pdGVkMScw # JQYDVQQLEx5uU2hpZWxkIFRTUyBFU046NTIxQS0wNUUwLUQ5NDcxJTAjBgNVBAMT # HE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2WiIwoBATAHBgUrDgMCGgMVAIyT # ny2W94r4qS97Ei5VhWy61o5koIGDMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNV # BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv # c29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAg # UENBIDIwMTAwDQYJKoZIhvcNAQELBQACBQDr0GLXMCIYDzIwMjUwNTE1MTI1NTUx # WhgPMjAyNTA1MTYxMjU1NTFaMHQwOgYKKwYBBAGEWQoEATEsMCowCgIFAOvQYtcC # AQAwBwIBAAICMWswBwIBAAICEvIwCgIFAOvRtFcCAQAwNgYKKwYBBAGEWQoEAjEo # MCYwDAYKKwYBBAGEWQoDAqAKMAgCAQACAwehIKEKMAgCAQACAwGGoDANBgkqhkiG # 9w0BAQsFAAOCAQEAQ77ymQlS5WTYSI/42woNhO1U287Q03VjI2swhoYE8vP8PbLQ # ksYzdwLhETzTBzV3BuUhKGr8IhTxWvrcgkxxePb0XlPH98Qrg72DtEJazn+/7vWT # Lw5B8fcLqUi7q241MCd8HkW0TPEVdho3ps9RUhNKf0Q9e+rc1cIDt1sgG112Sh5v # 5fbMBDx0TtS+uq0MHp3MoNzuacBToBGTtiqHg58LBFXq+mlQX/+u8nbBdvXW8Sp3 # jqKgVBf7bgyqtTu1CeMqkgs4nhLyfnJSdl4XnFvEBYsmVG7Ib7JXooFbqQzrDzG+ # h1PnK34MJkjRIEIhAU0gESmcXLqUvYF94Hf2KDGCBA0wggQJAgEBMIGTMHwxCzAJ # BgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25k # MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jv # c29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAACAAvXqn8bKhdWAAEAAAIAMA0G # CWCGSAFlAwQCAQUAoIIBSjAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwLwYJ # KoZIhvcNAQkEMSIEICPdQfTTxLG5I7RS0ywoHmlzaQXdw+tLve0EQfohTTGaMIH6 # BgsqhkiG9w0BCRACLzGB6jCB5zCB5DCBvQQg1Mjt7DWd27qwTQxlAleDXzNoB+Gl # rkbnSJP/SgJP2ScwgZgwgYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2Fz # aGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENv # cnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAx # MAITMwAAAgAL16p/GyoXVgABAAACADAiBCAk69hnk2ypzl3qOs+j+isY5phoHVvq # PjIHX+UbSXzwdTANBgkqhkiG9w0BAQsFAASCAgCItx5OPv5LKkdp3Cl+HVAA8Je8 # tJ0HwizAVnlxlYcDiR3Mt6z2hkmFfyQ2n3DD6CAwYM8wEu9RjOs9GT60Hivj/Fm7 # FaRIw79DDYNwFQSNiaU50y76k5o9CLYwzxj3AmxpYRsBoEQniBtouxAlXMvZbiPt # wblz8wVHVJKtM3Of5ijOlr4mQqxHI39kiqFFyJLDOZNuZRvELlyXAXGmhHKp0+1I # yKltv0UtHoIdoMPSk3aa6kVvH41MLuwzbFQNqWJJuinBiEucAsWIJSGpp6DUptOj # VcammHC83eGf5g8DcEa2hi1y6W2/1pHTsv9YqM2acz7SHI5Eg3nAwKif9O935cUt # 2uCVOyAAu2ZVFOn8YZPmp84MC1rwYIMJ6dTb1poAAZgdk3HV+j7hBjkS3Rwv7/tA # JWJ1IeUzf6jqyc3pmyA97rbcvkdWipoiMFgisQtK5/1GDkPM6TFev3pDPpN35vGf # WQBGSO/tdE25cOFf7ydeL72CfIzi562hxK7i3rDtX9A/d5QZGiTb0EsIvMc1uS21 # uaOgGHjxND8kwWD8g1ARVKzYBCr2yAReCJsFP4e/w+SdmFE4+KCF9M86VybuzVl8 # lrtKC26crdLECw/PuFg6bBbQ6Fjoby/bpHLrHZotbwVEmoMfQtDW8dDi2+YtiKjz # 7zUkl8gm5lckKyeI/w== # SIG # End signature block |