Public/NerdctlTools.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-NerdctlLatestVersion {
    $latestVersion = Get-LatestToolVersion -Tool "nerdctl"
    return $latestVersion
}

function Get-NerdctlDependencies($dependencies) {
    if (!$dependencies) {
        return
    }

    $nerdctlDependencies = @("Containerd", "Buildkit", "WinCNIPlugin")
    if ($Dependencies -and $Dependencies -contains "All") {
        $dependencies = $nerdctlDependencies
    }

    foreach ($dependency in $dependencies) {
        if (-not ($nerdctlDependencies -contains $dependency)) {
            Throw "Invalid dependency option: $dependency. Allowed options: All, Containerd, Buildkit, WinCNIPlugin"
        }
    }

    return $dependencies
}

function Install-NerdctlDependencies {
    param(
        [String[]]$Dependencies,
        [string]$OsArch,
        [Switch]$Force
    )

    foreach ($dependency in $Dependencies) {
        $InstallCommand = "Install-$dependency"
        try {
            & $InstallCommand -OSArchitecture $OsArch -Force:$Force -Confirm:$false
        }
        catch {
            Write-Error "Installation failed for $dependency. $_"
        }
    }
}

function Install-Nerdctl {
    [CmdletBinding(
        SupportsShouldProcess = $true,
        ConfirmImpact = 'High'
    )]
    param(
        [string]
        [parameter(HelpMessage = "nerdctl version to use. Defaults to 'latest'")]
        $Version = "latest",

        [String]
        [parameter(HelpMessage = "Path to install nerdctl. Defaults to ~\program files\nerdctl")]
        $InstallPath = "$Env:ProgramFiles\nerdctl",

        [String]
        [parameter(HelpMessage = "Path to download files. Defaults to user's Downloads folder")]
        $DownloadPath = "$HOME\Downloads",

        [String[]]
        [parameter(HelpMessage = "Specify nerdctl dependencies (All, Containerd, Buildkit, WinCNIPlugin) to install")]
        $Dependencies,

        [string]
        [Parameter(HelpMessage = 'OS architecture to download files for. Default is $env:PROCESSOR_ARCHITECTURE')]
        [ValidateSet('amd64', '386', "arm", "arm64")]
        $OSArchitecture = $env:PROCESSOR_ARCHITECTURE,

        [Switch]
        [parameter(HelpMessage = "Installs nerdctl (and its dependencies if specified) even if the tool already exists at the specified path")]
        $Force
    )

    begin {
        # Check if nerdctl is already installed
        $isInstalled = -not (Test-EmptyDirectory -Path $InstallPath)
        $toInstall = @("nerdctl")

        $dependencies = Get-NerdctlDependencies -Dependencies $dependencies
        if ($dependencies) {
            $toInstall += $dependencies
        }

        $WhatIfMessage = "nerdctl will be installed at $installPath"
        if ($isInstalled) {
            $WhatIfMessage = "nerdctl will be uninstalled and reinstalled at $InstallPath"
        }
        if ($dependencies) {
            $WhatIfMessage = "nerdctl and its dependencies (Containerd, Buildkit, WinCNIPlugin) will be installed"
        }
    }

    process {
        if ($PSCmdlet.ShouldProcess($env:COMPUTERNAME, $WhatIfMessage)) {
            # Check if nerdctl already exists at specified location
            if ($isInstalled) {
                $errMsg = "nerdctl 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-Nerdctl -Path "$InstallPath" -Confirm:$false -Force:$Force | Out-Null
                }
                catch {
                    Throw "nerdctl installation failed. $_"
                }
            }

            # Get nerdctl version to install if not specified
            if (!$Version) {
                $Version = Get-NerdctlLatestVersion
            }
            $Version = $Version.TrimStart('v')

            Write-Output "Downloading and installing nerdctl v$Version at $InstallPath"

            # Download files
            $downloadParams = @{
                ToolName           = "nerdctl"
                Repository         = "$NERDCTL_REPO"
                Version            = $Version
                OSArchitecture     = $OSArchitecture
                DownloadPath       = $DownloadPath
                ChecksumSchemaFile = $null
                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 and install tool
            $params = @{
                Feature     = "nerdctl"
                InstallPath = $InstallPath
                SourceFile  = $sourceFile
                EnvPath     = $InstallPath
                cleanup     = $true
            }
            Install-RequiredFeature @params

            Write-Output "nerdctl v$version successfully installed at $InstallPath"
            Write-Output "For nerdctl usage: run 'nerdctl -h'`n"

            # Install dependencies
            Write-Output "Installing nerdctl dependencies: $toinstall"
            Install-NerdctlDependencies -Dependencies $dependencies -OsArch $OSArchitecture -Force:$true
        }
        else {
            # Code that should be processed if doing a WhatIf operation
            # Must NOT change anything outside of the function / script
            return
        }
    }
}


# TODO: Implement this
function Initialize-NerdctlToml {
    param(
        [parameter(HelpMessage = "Toml path. Defaults to ~\AppData\nerdctl\nerdctl.toml")]
        [String]$Path = "$env:APPDATA\nerdctl\nerdctl.toml"
    )

    # https://github.com/containerd/nerdctl/blob/main/docs/config.md
    $nerdctlConfig = @"
{}
"@


    $nerdctlConfig | Set-Content $Path -Force
}

function Uninstall-Nerdctl {
    [CmdletBinding(
        SupportsShouldProcess = $true,
        ConfirmImpact = 'High'
    )]
    param(
        [parameter(HelpMessage = "nerdctl path")]
        [String]$Path,

        [parameter(HelpMessage = "Bypass confirmation to uninstall nerdctl")]
        [Switch] $Force
    )

    begin {
        $tool = 'nerdctl'
        if (!$Path) {
            $Path = Get-DefaultInstallPath -Tool "nerdctl"
        }

        $WhatIfMessage = "nerdctl will be uninstalled from $Path"
    }

    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($Path, 'Are you sure you want to uninstall nerdctl?')
            }

            if (!$consent) {
                Throw "$tool uninstallation cancelled."
            }

            Write-Warning "Uninstalling preinstalled $tool at the path $path"
            try {
                Uninstall-NerdctlHelper -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-NerdctlHelper {
    param(
        [ValidateNotNullOrEmpty()]
        [parameter(Mandatory = $true, HelpMessage = "nerdctl path")]
        [String]$Path
    )

    if (Test-EmptyDirectory -Path $Path) {
        Write-Error "nerdctl does not exist at $Path or the directory is empty."
        return
    }

    # Remove the folder where nerdctl is installed and related folders
    Remove-Item -Path $Path -Recurse -Force

    # Remove ProgramData files
    Uninstall-ProgramFiles "$ENV:ProgramData\nerdctl"

    # Remove from env path
    Remove-FeatureFromPath -Feature "nerdctl"

    Write-Output "Successfully uninstalled nerdctl."
}

Export-ModuleMember -Function Get-NerdctlLatestVersion
Export-ModuleMember -Function Install-Nerdctl
Export-ModuleMember -Function Uninstall-Nerdctl
Export-ModuleMember -Function Uninstall-NerdctlHelper
Export-ModuleMember -Function Install-NerdctlDependencies

# SIG # Begin signature block
# MIIoRAYJKoZIhvcNAQcCoIIoNTCCKDECAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAoMYsz4eNT+3qc
# /Q/DJY08mR+ImUp3e6a+KVTpod+yqaCCDYswggYJMIID8aADAgECAhMzAAAD9LjE
# 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/Xmfwb1tbWrJUnMTDXpQzTGCGg8wghoLAgEB
# MIGVMH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQH
# EwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNV
# BAMTH01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTECEzMAAAP0uMRd4U5w
# tn4AAAAAA/QwDQYJYIZIAWUDBAIBBQCggbAwGQYJKoZIhvcNAQkDMQwGCisGAQQB
# gjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkE
# MSIEIAisBHhgKWN+uZ2aYgfctU6t0lp9fbD/QwQVbbPPkh2qMEQGCisGAQQBgjcC
# AQwxNjA0oBSAEgBNAGkAYwByAG8AcwBvAGYAdKEcgBpodHRwczovL3d3dy5taWNy
# b3NvZnQuY29tIDANBgkqhkiG9w0BAQEFAASCAQAKVhjAhc4lTYUCE9pZbOLYaoXW
# TX7sKGks7cwEgEI+Kj2YdAIMYq8M06jRFggelBHGp0KMtBGMCUbl+YrUSfiWJ+l4
# PvvtwTq+edbn3EZR9h6Ca7RjTdKcie7GAV9ddJTZ9W4PgA3Jh0jO/QVlwFeYGfEQ
# +OcpnbN6Kls3FL3vB76cjNjFljP64e7TwgbBtLmcJ0BRO1vvv3P3Dkwo/wEmUwFL
# p4b4vH+6iRraEBBnsEWWPlITe/KV+vOHzexTmik/IOie3W44wjseK2ijokcKv4Bh
# Fnmo8Pw64vTrVHunvI8c9z+xLA7qD3S7knWU/X2mVJzosdFw/Qk4dxAEAQdRoYIX
# lzCCF5MGCisGAQQBgjcDAwExgheDMIIXfwYJKoZIhvcNAQcCoIIXcDCCF2wCAQMx
# DzANBglghkgBZQMEAgEFADCCAVIGCyqGSIb3DQEJEAEEoIIBQQSCAT0wggE5AgEB
# BgorBgEEAYRZCgMBMDEwDQYJYIZIAWUDBAIBBQAEIHz5FASWBY0ywFNzLt5XoX07
# /3K/DxPBLN6K9SR2ObRnAgZoJdLn01EYEzIwMjUwNTE1MTYxOTUyLjY1MVowBIAC
# AfSggdGkgc4wgcsxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw
# DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x
# JTAjBgNVBAsTHE1pY3Jvc29mdCBBbWVyaWNhIE9wZXJhdGlvbnMxJzAlBgNVBAsT
# Hm5TaGllbGQgVFNTIEVTTjpBNDAwLTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9z
# b2Z0IFRpbWUtU3RhbXAgU2VydmljZaCCEe0wggcgMIIFCKADAgECAhMzAAACAnlQ
# dCEUfbihAAEAAAICMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1w
# IFBDQSAyMDEwMB4XDTI1MDEzMDE5NDI0NFoXDTI2MDQyMjE5NDI0NFowgcsxCzAJ
# BgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25k
# MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJTAjBgNVBAsTHE1pY3Jv
# c29mdCBBbWVyaWNhIE9wZXJhdGlvbnMxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVT
# TjpBNDAwLTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAg
# U2VydmljZTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALd5Knpy5xQY
# 6Rw+Di8pYol8RB6yErZkGxhTW0Na9C7ov2Wn52eqtqMh014fUc3ejPeKIagla43Y
# dU1mRw63fxpYZ5szSBRQ60+O4uG47l3rtilCwcEkBaFy978xV2hA+PWeOICNKI6s
# vzEVqsUsjjpEfw14OEA9dwmlafsAjMLIiNk5onYNYD7pDA3PCqMGAil/WFYXCoe8
# 8R53LSei1du1Z9P28JIv2x0Mror8cf0expjnAuZRQHtJ+4sajU5YSbownIbaOLGq
# L03JGjKl0Xx1HKNbEpGXYnHC9t62UNOKjrpeWJM5ySrZGAz5mhxkRvoSg5213Rcq
# HcvPHb0CEfGWT7p4jBq+Udi44tkMqh085U3qPUgn1uuiVjqZluhDnU6p7mcQzmH9
# YlfbwYtmKgSQk3yo57k/k/ZjH0eg6ou6BfTSoLPGrgEObzEfzkcrG8oI7kqKSilp
# EYa1CVeMPK6wxaWsdzJK3noOEvh1xWeft0W8vnTO9CUVkyFWh6FZJCSRa5SUIKog
# 6tN7tFuadt0miwf7uUL6fneCcrLg6hnO5R6rMKdIHUk1c8qcmiM/cN7nHCymLm1S
# 9AU1+V8ZOyNmBACAMF2D8M7RMaAtEMq9lAJnmoi5elBHKDfvJznV73nPxTabKxTR
# edKlZ6KAeqTI4C0N9wimrka/sdX51rZHAgMBAAGjggFJMIIBRTAdBgNVHQ4EFgQU
# 2ga5tQ+M/Z/yJ+Qgq/DLWuVIdNkwHwYDVR0jBBgwFoAUn6cVXQBeYl2D9OXSZacb
# UzUZ6XIwXwYDVR0fBFgwVjBUoFKgUIZOaHR0cDovL3d3dy5taWNyb3NvZnQuY29t
# L3BraW9wcy9jcmwvTWljcm9zb2Z0JTIwVGltZS1TdGFtcCUyMFBDQSUyMDIwMTAo
# MSkuY3JsMGwGCCsGAQUFBwEBBGAwXjBcBggrBgEFBQcwAoZQaHR0cDovL3d3dy5t
# aWNyb3NvZnQuY29tL3BraW9wcy9jZXJ0cy9NaWNyb3NvZnQlMjBUaW1lLVN0YW1w
# JTIwUENBJTIwMjAxMCgxKS5jcnQwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAK
# BggrBgEFBQcDCDAOBgNVHQ8BAf8EBAMCB4AwDQYJKoZIhvcNAQELBQADggIBAIPz
# doVBTE3fseQ6gkMzWZocVlVQZypNBw+c4PpShhEyYMq/QZpseUTzYBiAs+5WW6Sf
# se0k8XbPSOdOAB9EyfbokUs8bs79dsorbmGsE8nfSUG7CMBNW3nxQDUFajuWyafK
# u6v/qHwAXOtfKte2W/NBippFhj2TRQVjkYz6f1hoQQrYPbrx75r4cOZZ761gvYf7
# 07hDUxAtqD5yI3AuSP/5CXGleJai70q8A/S0iT58fwXfDDlU5OL1pn36o+OzPDfU
# fid22K8FlofmzlugmYfYlu0y5/bLuFJ0l0TRRbYHQURk8siZ6aUqGyUk1WoQ7tE+
# CXtzzVC5VI7nx9+mZvC1LGFisRLdWw+CVef04MXsOqY8wb8bKwHij9CSk1Sr7BLt
# s5FM3Oocy0f6it3ZhKZr7VvJYGv+LMgqCA4J0TNpkN/KbXYYzprhL4jLoBQinv8o
# ikCZ9Z9etwwrtXsQHPGh7OQtEQRYjhe0/CkQGe05rWgMfdn/51HGzEvS+DJruM1+
# s7uiLNMCWf/ZkFgH2KhR6huPkAYvjmbaZwpKTscTnNRF5WQgulgoFDn5f/yMU7X+
# lnKrNB4jX+gn9EuiJzVKJ4td8RP0RZkgGNkxnzjqYNunXKcr1Rs2IKNLCZMXnT1i
# f0zjtVCzGy/WiVC7nWtVUeRI2b6tOsvArW2+G/SZMIIHcTCCBVmgAwIBAgITMwAA
# ABXF52ueAptJmQAAAAAAFTANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMx
# EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT
# FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3Qg
# Q2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMjEwOTMwMTgyMjI1WhcNMzAw
# OTMwMTgzMjI1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQ
# MA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u
# MSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCAiIwDQYJ
# KoZIhvcNAQEBBQADggIPADCCAgoCggIBAOThpkzntHIhC3miy9ckeb0O1YLT/e6c
# BwfSqWxOdcjKNVf2AX9sSuDivbk+F2Az/1xPx2b3lVNxWuJ+Slr+uDZnhUYjDLWN
# E893MsAQGOhgfWpSg0S3po5GawcU88V29YZQ3MFEyHFcUTE3oAo4bo3t1w/YJlN8
# OWECesSq/XJprx2rrPY2vjUmZNqYO7oaezOtgFt+jBAcnVL+tuhiJdxqD89d9P6O
# U8/W7IVWTe/dvI2k45GPsjksUZzpcGkNyjYtcI4xyDUoveO0hyTD4MmPfrVUj9z6
# BVWYbWg7mka97aSueik3rMvrg0XnRm7KMtXAhjBcTyziYrLNueKNiOSWrAFKu75x
# qRdbZ2De+JKRHh09/SDPc31BmkZ1zcRfNN0Sidb9pSB9fvzZnkXftnIv231fgLrb
# qn427DZM9ituqBJR6L8FA6PRc6ZNN3SUHDSCD/AQ8rdHGO2n6Jl8P0zbr17C89XY
# cz1DTsEzOUyOArxCaC4Q6oRRRuLRvWoYWmEBc8pnol7XKHYC4jMYctenIPDC+hIK
# 12NvDMk2ZItboKaDIV1fMHSRlJTYuVD5C4lh8zYGNRiER9vcG9H9stQcxWv2XFJR
# XRLbJbqvUAV6bMURHXLvjflSxIUXk8A8FdsaN8cIFRg/eKtFtvUeh17aj54WcmnG
# rnu3tz5q4i6tAgMBAAGjggHdMIIB2TASBgkrBgEEAYI3FQEEBQIDAQABMCMGCSsG
# AQQBgjcVAgQWBBQqp1L+ZMSavoKRPEY1Kc8Q/y8E7jAdBgNVHQ4EFgQUn6cVXQBe
# Yl2D9OXSZacbUzUZ6XIwXAYDVR0gBFUwUzBRBgwrBgEEAYI3TIN9AQEwQTA/Bggr
# BgEFBQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9Eb2NzL1Jl
# cG9zaXRvcnkuaHRtMBMGA1UdJQQMMAoGCCsGAQUFBwMIMBkGCSsGAQQBgjcUAgQM
# HgoAUwB1AGIAQwBBMAsGA1UdDwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1Ud
# IwQYMBaAFNX2VsuP6KJcYmjRPZSQW9fOmhjEMFYGA1UdHwRPME0wS6BJoEeGRWh0
# dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0Nl
# ckF1dF8yMDEwLTA2LTIzLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKG
# Pmh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0
# XzIwMTAtMDYtMjMuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQCdVX38Kq3hLB9nATEk
# W+Geckv8qW/qXBS2Pk5HZHixBpOXPTEztTnXwnE2P9pkbHzQdTltuw8x5MKP+2zR
# oZQYIu7pZmc6U03dmLq2HnjYNi6cqYJWAAOwBb6J6Gngugnue99qb74py27YP0h1
# AdkY3m2CDPVtI1TkeFN1JFe53Z/zjj3G82jfZfakVqr3lbYoVSfQJL1AoL8ZthIS
# EV09J+BAljis9/kpicO8F7BUhUKz/AyeixmJ5/ALaoHCgRlCGVJ1ijbCHcNhcy4s
# a3tuPywJeBTpkbKpW99Jo3QMvOyRgNI95ko+ZjtPu4b6MhrZlvSP9pEB9s7GdP32
# THJvEKt1MMU0sHrYUP4KWN1APMdUbZ1jdEgssU5HLcEUBHG/ZPkkvnNtyo4JvbMB
# V0lUZNlz138eW0QBjloZkWsNn6Qo3GcZKCS6OEuabvshVGtqRRFHqfG3rsjoiV5P
# ndLQTHa1V1QJsWkBRH58oWFsc/4Ku+xBZj1p/cvBQUl+fpO+y/g75LcVv7TOPqUx
# UYS8vwLBgqJ7Fx0ViY1w/ue10CgaiQuPNtq6TPmb/wrpNPgkNWcr4A245oyZ1uEi
# 6vAnQj0llOZ0dFtq0Z4+7X6gMTN9vMvpe784cETRkPHIqzqKOghif9lwY1NNje6C
# baUFEMFxBmoQtB1VM1izoXBm8qGCA1AwggI4AgEBMIH5oYHRpIHOMIHLMQswCQYD
# VQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEe
# MBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQLExxNaWNyb3Nv
# ZnQgQW1lcmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxkIFRTUyBFU046
# QTQwMC0wNUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNl
# cnZpY2WiIwoBATAHBgUrDgMCGgMVAEmJSGkJYD/df+NnIjLTJ7pEnAvOoIGDMIGA
# pH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcT
# B1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UE
# AxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwDQYJKoZIhvcNAQELBQAC
# BQDr0FFkMCIYDzIwMjUwNTE1MTE0MTI0WhgPMjAyNTA1MTYxMTQxMjRaMHcwPQYK
# KwYBBAGEWQoEATEvMC0wCgIFAOvQUWQCAQAwCgIBAAICAIwCAf8wBwIBAAICEfYw
# CgIFAOvRouQCAQAwNgYKKwYBBAGEWQoEAjEoMCYwDAYKKwYBBAGEWQoDAqAKMAgC
# AQACAwehIKEKMAgCAQACAwGGoDANBgkqhkiG9w0BAQsFAAOCAQEAqqUjDKJUQO5S
# 5oWMRobJDrdADBYfc7j4mRkYrEUPK4jrYzGKExolvKc4XPg4FTbNNC26hewAakPw
# Yzv/W7lVUvT9bC/SDlDbHIBlJ8fn5W9xERKAGJW/RsAO419HsXOWuRUHfPDKxRXk
# 3keFzUKCWZ/W5lvKPbcuWsOBEgeRopyjtt5WL8HJrI7utXSEMRg5EF4rza6a+mmK
# 5o93iGgHcfSdYw6JxD6/BIwyu7Ym0wGrlfTGjC7b3lVvnD2ToFSmLdZdpJtg4AtH
# PF/IEvV0O8gIFtuncGwmu844MkSqa17rc80SsUDnYDOOdyhkBJwAW26ikitsppQr
# 3ny6MonygTGCBA0wggQJAgEBMIGTMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpX
# YXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQg
# Q29ycG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAy
# MDEwAhMzAAACAnlQdCEUfbihAAEAAAICMA0GCWCGSAFlAwQCAQUAoIIBSjAaBgkq
# hkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwLwYJKoZIhvcNAQkEMSIEICGSjw9zFp25
# yZ1oFiPC5wl5t/FOkwjAvND8ww1IhQv3MIH6BgsqhkiG9w0BCRACLzGB6jCB5zCB
# 5DCBvQQg843qARgHlsvNcta5SYvxl3zFcCypeSx50XKiV8yUX+wwgZgwgYCkfjB8
# MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVk
# bW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1N
# aWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAgJ5UHQhFH24oQABAAAC
# AjAiBCCneRkQYbjGMp3Wxu2U5Dh+xgPqhLWg/GvX2xjkcWuAyzANBgkqhkiG9w0B
# AQsFAASCAgAFjSu0CRgEuGt0/8TnqHBXHSXZCJQrrQyrt3Vb+bQPNdZ4q8MuCQXl
# LaWU3QD9mm2zU1ZFjBh7gAbyORBNUpCIjYToSuBvxRmQw6v2GxCeH77r4ucwuvWI
# DqWwemtcA217wvqSO3LarpoSNTMHSCynEaYKJkWwd7YTkcrZb8l/Fl7bk1MABdpA
# gFf087boQgOGgfR/XkTjl00BMNBHHKYxL5E/XKJQxd6iAPMfeyRfZXSeR3YuwzX/
# lY5hhbdlT42ikXsQ8uVZ/eE7RyWyBagdy2RJead72w2YB4JPwhi858GaYsEjJGIi
# O9S/DJkW9hA0DmgREqtqpvRiHACcLVBgWuxpiXV5uLLtwZ675QzfVLJRTRIXeAro
# GnQv63l7I4cnCdV0DqypDZlLOI9qw+A3AifWZQ1V+J6Unjx017VDcEB7kdQlUn65
# W2wHMXCHZk5DpGmpJF7+xOAgQ51CxdvbYakV7XUkneOKSepFAcQvkpnixTz6gBsH
# fQd/hrQIB6I7o/f63+xiB0UIKzcnjrko68/Osu3u56wVH77bY0ueaiRiR3ktRmAq
# k2B26kuM8QxcoaO2P3SH+oRuOm/MmxSbnYHQc9Fhm405qC+POzCUgL0qUxZN5pbe
# nPtPQUTQcz7Io8Dx/34onOU2MPTYk4fl4nNEOEiOBtZJhUOOvs5W6w==
# SIG # End signature block