Import.psm1

#-----------------------------------------------------------------------
# Copyright (c) Microsoft Corporation. All rights reserved.
#-----------------------------------------------------------------------

# .EXTERNALHELP Import.psm1-Help.xml

function Import-AzsMarketplaceItem {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string] $RepositoryDir,

        [Parameter(Mandatory = $false)]
        [string] $JournalDir,

        [Parameter(Mandatory = $false)]
        [string[]] $ProductName,

        [Parameter(Mandatory = $false)]
        [ValidateSet('Upload', 'Import', 'Cleanup')]
        [string] $From = 'Initialize',

        [Parameter(Mandatory = $false)]
        [ValidateSet('Upload', 'Import', 'Cleanup')]
        [string] $UpTo = 'Cleanup',

        [Parameter(Mandatory = $false)]
        [ValidateRange(5, 60)]
        [int] $SleepInterval = 60
    )

    function Upload-AllProducts {
        param ()

        function Initialize-StorageAccount {
            param ()

            Start-Activity 'Initialize-StorageAccount'

            Select-AzSubscription -Subscription 'Default Provider Subscription' -ErrorAction Stop | Out-Null

            New-AzResourceGroup -Name $ResourceGroupName -Location $Location -Force -ErrorAction Stop | Out-Null

            $sa = Get-AzStorageAccount -ResourceGroupName $ResourceGroupName -Name $StorageAccountName -ErrorAction SilentlyContinue

            if (-not $sa) {
                $sa = New-AzStorageAccount -ResourceGroupName $ResourceGroupName -Name $StorageAccountName -Location $Location -SkuName Standard_LRS -ErrorAction Stop
            }

            Stop-Activity

            return $sa.Context.ConnectionString
        }

        function Invoke-Upload {
            param (
                [ValidateNotNullOrEmpty()]
                [string] $ConnectionString
            )

            Start-Activity 'Invoke-Upload'

            while ($true) {
                & "$PSScriptRoot\SyndicationClient.exe" upload $RepositoryDir $JournalDir $ConnectionString

                if ($LASTEXITCODE -eq 0) {
                    break
                }

                Write-Verbose "Sleep for $SleepInterval seconds" -Verbose
                Start-Sleep -Seconds $SleepInterval
            }

            Stop-Activity
        }

        #-----------------------------------------------------------------------

        Start-Activity 'Upload-AllProducts'

        $productList = Get-ProductList -ProductNames $ProductName

        $uploadJobInfo = @{
            productNames = @() + ($productList | ForEach-Object { $_.ProductName })
        }
        $uploadJobInfo | ConvertTo-Json -Depth 99 | Set-Content -LiteralPath "$JournalDir\.upload-info.json" -Encoding UTF8

        $connectionString = Initialize-StorageAccount

        Invoke-Upload -ConnectionString $connectionString

        Stop-Activity
    }

    #-----------------------------------------------------------------------

    function Import-AllProducts {
        param ()

        function Import-Product {
            param (
                [psobject] $ProductDetails
            )

            Start-Activity "Import-Product, productName = $($ProductDetails.ProductName)"

            $productId = "$ActivationResourceId/downloadedProducts/$($productDetails.ProductName)"
            $requestUri = "$($productId)?api-version=2016-01-01"
            $response = Invoke-AzsResourceManager -Method GET -Uri $requestUri

            if ($response.StatusCode -eq [System.Net.HttpStatusCode]::NotFound) {
                $product = $null
            }
            else {
                Ensure-SuccessStatusCode -Response $response
                $product = $response.Content | ConvertFrom-Json
            }

            Write-Verbose "Provisioning state of $($productDetails.ProductName): $($product.properties.provisioningState)" -Verbose

            if (-not $product -or $product.properties.provisioningState -eq 'Failed') {
                Write-Verbose 'Initializing product import...' -Verbose

                $requestUri = "$($productId)?api-version=2016-01-01"
                $payload = Get-Content -LiteralPath "$JournalDir\$($productDetails.ProductName)\payload.json" -Raw | ConvertFrom-Json
                Invoke-AzsResourceManager -Method PUT -Uri $requestUri -Body @{ properties = $payload; } -ThrowOnError | Out-Null
            }

            Stop-Activity
        }

        function Wait-ForProducts {
            param (
                [psobject[]] $ProductList
            )

            Start-Activity 'Wait-ForProducts'

            $timeout = [timespan]::FromHours(6)
            $stopwatch = [System.Diagnostics.Stopwatch]::StartNew()

            while ($true) {
                Write-Verbose "[$(Get-Date -Format 's')] Get product import status" -Verbose

                $allDone = $true

                foreach ($productDetails in $ProductList) {
                    $productId = "$ActivationResourceId/downloadedProducts/$($productDetails.ProductName)"
                    $requestUri = "$($productId)?api-version=2016-01-01"
                    $response = Invoke-AzsResourceManager -Method GET -Uri $requestUri -ThrowOnError
                    $product = $response.Content | ConvertFrom-Json

                    Write-Verbose "Provisioning state of $($productDetails.ProductName): $($product.properties.provisioningState)" -Verbose

                    if ($product.properties.provisioningState -eq 'Failed') {
                        throw "Failed to import product, productId: $productId."
                    }

                    if ($product.properties.provisioningState -ne 'Succeeded') {
                        $allDone = $false
                    }
                }

                if ($allDone) {
                    break
                }

                if ($stopwatch.Elapsed -gt $timeout) {
                    throw 'Product import didn''t complete within the allotted time.'
                }

                Write-Verbose "Sleep for $SleepInterval seconds" -Verbose
                Start-Sleep -Seconds $SleepInterval
            }

            Stop-Activity
        }

        #-----------------------------------------------------------------------

        Start-Activity 'Import-AllProducts'

        $uploadJobInfo = Get-Content "$JournalDir\.upload-info.json" -Raw | ConvertFrom-Json
        $productList = Get-ProductList -ProductNames $uploadJobInfo.productNames

        foreach ($productDetails in $productList) {
            Import-Product -ProductDetails $productDetails
        }

        Wait-ForProducts -ProductList $productList

        Stop-Activity
    }

    #-----------------------------------------------------------------------

    function Invoke-Cleanup {
        param ()

        Start-Activity 'Invoke-Cleanup'

        $rg = Get-AzResourceGroup -Name $ResourceGroupName -Location $Location -ErrorAction SilentlyContinue

        if ($rg) {
            Remove-AzResourceGroup -Name $ResourceGroupName -Force
        }

        if (Test-Path $JournalDir) {
            Remove-Item $JournalDir -Recurse -Force
        }

        Stop-Activity
    }

    #-----------------------------------------------------------------------

    function Resolve-Activation {
        param ()

        $activation = Get-AzResource -ResourceType Microsoft.AzureBridge.Admin/activations | Select-Object -First 1

        if (-not $activation) {
            throw 'Azure Stack activation not found. Make sure your stamp is activated and the selected subscription is ''Default Provider Subscription''.'
        }

        return $activation.ResourceId
    }

    function Get-ProductList {
        param (
            [string[]]$ProductNames
        )

        $products = @()

        if ($ProductNames) {
            $productDirs = $ProductNames | ForEach-Object { [System.IO.Path]::Combine($RepositoryDir, $_) }
        }
        else {
            $productDirs = Get-ChildItem -LiteralPath $RepositoryDir -Directory | ForEach-Object { $_.FullName }
        }

        foreach ($productDir in $productDirs) {
            $productDetailsFileName = [System.IO.Path]::Combine($productDir, "productDetails.json")
            $productDetails = Get-Content -LiteralPath $productDetailsFileName -Raw | ConvertFrom-Json
            $products += $productDetails
        }

        return $products
    }

    #-----------------------------------------------------------------------

    $Steps = @{
        Upload = 0
        Import = 1
        Cleanup = 2
    }
    $FromValue = $Steps[$From]
    $UpToValue = $Steps[$UpTo]

    $Location = (Get-AzLocation)[0].Location
    $ResourceGroupName = 'syndicationtemp-rg'
    $StorageAccountName = 'syndicationtempsa'

    $RepositoryDir = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($RepositoryDir)
    New-Item -Path $RepositoryDir -ItemType Directory -Force | Out-Null

    if (-not $JournalDir) {
        $JournalDir = [System.IO.Path]::Combine([System.IO.Path]::GetTempPath(), [guid]::NewGuid().ToString())
    }
    else {
        $JournalDir = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($JournalDir)
    }
    New-Item -Path $JournalDir -ItemType Directory -Force | Out-Null

    $ActivationResourceId = Resolve-Activation

    Start-Activity 'Import'

    Write-Verbose "RepositoryDir: $RepositoryDir" -Verbose
    Write-Verbose "JournalDir: $JournalDir" -Verbose

    if ($FromValue -le $Steps.Upload -and $UpToValue -ge $Steps.Upload) {
        Upload-AllProducts
    }

    if ($FromValue -le $Steps.Import -and $UpToValue -ge $Steps.Import) {
        Import-AllProducts
    }

    if ($FromValue -le $Steps.Cleanup -and $UpToValue -ge $Steps.Cleanup) {
        Invoke-Cleanup
    }

    Stop-Activity
}

#-----------------------------------------------------------------------

$ErrorActionPreference = 'Stop'
$InformationPreference = 'Continue'

Import-Module "$PSScriptRoot\Activities.psm1" -Force -ErrorAction Stop
Import-Module "$PSScriptRoot\ResourceManager.psm1" -Force -DisableNameChecking -ErrorAction Stop

Export-ModuleMember -Function @('Import-AzsMarketplaceItem')

# SIG # Begin signature block
# MIIjlAYJKoZIhvcNAQcCoIIjhTCCI4ECAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCChT8IL0aNsw3jq
# 58KLzuFEj7IXeSyddiy15GwOdALuLqCCDXYwggX0MIID3KADAgECAhMzAAABhk0h
# daDZB74sAAAAAAGGMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjAwMzA0MTgzOTQ2WhcNMjEwMzAzMTgzOTQ2WjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQC49eyyaaieg3Xb7ew+/hA34gqzRuReb9svBF6N3+iLD5A0iMddtunnmbFVQ+lN
# Wphf/xOGef5vXMMMk744txo/kT6CKq0GzV+IhAqDytjH3UgGhLBNZ/UWuQPgrnhw
# afQ3ZclsXo1lto4pyps4+X3RyQfnxCwqtjRxjCQ+AwIzk0vSVFnId6AwbB73w2lJ
# +MC+E6nVmyvikp7DT2swTF05JkfMUtzDosktz/pvvMWY1IUOZ71XqWUXcwfzWDJ+
# 96WxBH6LpDQ1fCQ3POA3jCBu3mMiB1kSsMihH+eq1EzD0Es7iIT1MlKERPQmC+xl
# K+9pPAw6j+rP2guYfKrMFr39AgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUhTFTFHuCaUCdTgZXja/OAQ9xOm4w
# RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW
# MBQGA1UEBRMNMjMwMDEyKzQ1ODM4NDAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci
# tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG
# CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu
# Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0
# MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAEDkLXWKDtJ8rLh3d7XP
# 1xU1s6Gt0jDqeHoIpTvnsREt9MsKriVGKdVVGSJow1Lz9+9bINmPZo7ZdMhNhWGQ
# QnEF7z/3czh0MLO0z48cxCrjLch0P2sxvtcaT57LBmEy+tbhlUB6iz72KWavxuhP
# 5zxKEChtLp8gHkp5/1YTPlvRYFrZr/iup2jzc/Oo5N4/q+yhOsRT3KJu62ekQUUP
# sPU2bWsaF/hUPW/L2O1Fecf+6OOJLT2bHaAzr+EBAn0KAUiwdM+AUvasG9kHLX+I
# XXlEZvfsXGzzxFlWzNbpM99umWWMQPTGZPpSCTDDs/1Ci0Br2/oXcgayYLaZCWsj
# 1m/a0V8OHZGbppP1RrBeLQKfATjtAl0xrhMr4kgfvJ6ntChg9dxy4DiGWnsj//Qy
# wUs1UxVchRR7eFaP3M8/BV0eeMotXwTNIwzSd3uAzAI+NSrN5pVlQeC0XXTueeDu
# xDch3S5UUdDOvdlOdlRAa+85Si6HmEUgx3j0YYSC1RWBdEhwsAdH6nXtXEshAAxf
# 8PWh2wCsczMe/F4vTg4cmDsBTZwwrHqL5krX++s61sLWA67Yn4Db6rXV9Imcf5UM
# Cq09wJj5H93KH9qc1yCiJzDCtbtgyHYXAkSHQNpoj7tDX6ko9gE8vXqZIGj82mwD
# TAY9ofRH0RSMLJqpgLrBPCKNMIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq
# hkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
# EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
# bjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5
# IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEwOTA5WjB+MQswCQYDVQQG
# EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG
# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQg
# Q29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
# CgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+laUKq4BjgaBEm6f8MMHt03
# a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc6Whe0t+bU7IKLMOv2akr
# rnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4Ddato88tt8zpcoRb0Rrrg
# OGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+lD3v++MrWhAfTVYoonpy
# 4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nkkDstrjNYxbc+/jLTswM9
# sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6A4aN91/w0FK/jJSHvMAh
# dCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmdX4jiJV3TIUs+UsS1Vz8k
# A/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL5zmhD+kjSbwYuER8ReTB
# w3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zdsGbiwZeBe+3W7UvnSSmn
# Eyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3T8HhhUSJxAlMxdSlQy90
# lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS4NaIjAsCAwEAAaOCAe0w
# ggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRIbmTlUAXTgqoXNzcitW2o
# ynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD
# VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBa
# BgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny
# bC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsG
# AQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29t
# L3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MIGfBgNV
# HSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEFBQcCARYzaHR0cDovL3d3
# dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1hcnljcHMuaHRtMEAGCCsG
# AQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkAYwB5AF8AcwB0AGEAdABl
# AG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn8oalmOBUeRou09h0ZyKb
# C5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7v0epo/Np22O/IjWll11l
# hJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0bpdS1HXeUOeLpZMlEPXh6
# I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/KmtYSWMfCWluWpiW5IP0
# wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvyCInWH8MyGOLwxS3OW560
# STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBpmLJZiWhub6e3dMNABQam
# ASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJihsMdYzaXht/a8/jyFqGa
# J+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYbBL7fQccOKO7eZS/sl/ah
# XJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbSoqKfenoi+kiVH6v7RyOA
# 9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sLgOppO6/8MO0ETI7f33Vt
# Y5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtXcVZOSEXAQsmbdlsKgEhr
# /Xmfwb1tbWrJUnMTDXpQzTGCFXQwghVwAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw
# EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN
# aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp
# Z25pbmcgUENBIDIwMTECEzMAAAGGTSF1oNkHviwAAAAAAYYwDQYJYIZIAWUDBAIB
# BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO
# MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEIFxy3h/H4Nm28kByNqaWjjwm
# gTIKoLuGSxab2MtMiKeGMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A
# cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB
# BQAEggEAswNAp5Fh9+iYAnCZ/iXlGezSfbhS88h974uWPKQUoa+Xt7xDHtDxwOhc
# 8xH+NAhHyxnbKP1XC41dciMv4/gSZC9hIyZBoxHjR5uEG1e1FUtnvvOXFj4FCNn+
# NTc1szK3KnEg49hUM9v0+9MksUsxYCjxWmZP402ye416g2hiGulEroAYQGMEf2vz
# 6vmCpuqVKu6gvbZfya5/gSNsYldxh4VrYHf1rJ9nczBRvKgIQNxf6SkkRvyUmPRE
# ZPpOu5tCKpDFIEvmRppp4qc5hEzu/m6OAVxiqccUp8468+uDj3kb/Lrhvqd/M0Ha
# dmZQmrs/0ZzKN0ZjLqVkELnOxA5gb6GCEv4wghL6BgorBgEEAYI3AwMBMYIS6jCC
# EuYGCSqGSIb3DQEHAqCCEtcwghLTAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFZBgsq
# hkiG9w0BCRABBKCCAUgEggFEMIIBQAIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl
# AwQCAQUABCBdo6qWMzkicffhB/iRhO3lpZQGrGB16iCS7Eb6IcAWjgIGX7wJ5kCS
# GBMyMDIwMTIwNDAwMDk1NC4xMDRaMASAAgH0oIHYpIHVMIHSMQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJl
# bGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNO
# OkEyNDAtNEI4Mi0xMzBFMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBT
# ZXJ2aWNloIIOTTCCBPkwggPhoAMCAQICEzMAAAE/4X7to9j/v9kAAAAAAT8wDQYJ
# KoZIhvcNAQELBQAwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
# EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
# bjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAwHhcNMjAx
# MDE1MTcyODI2WhcNMjIwMTEyMTcyODI2WjCB0jELMAkGA1UEBhMCVVMxEzARBgNV
# BAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jv
# c29mdCBDb3Jwb3JhdGlvbjEtMCsGA1UECxMkTWljcm9zb2Z0IElyZWxhbmQgT3Bl
# cmF0aW9ucyBMaW1pdGVkMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjpBMjQwLTRC
# ODItMTMwRTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2VydmljZTCC
# ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAO1F1U5lbR+JB2ejAO7KRMFd
# Lm5arpdDoKyH3TeMURCjsDo7udkW/c0a0xZCt+PCy6PuKGtY1kjArogRhjxEuyEJ
# 368jnB1kbhLaY0DI8UqEMSMV6dzgixF/W2TROg92bsMG4ufWj86pBaC/XlauTNsj
# YPDCHszV7tt/QOHn0agmPw4X68PyCO8cbgqa9qFV0e02EVupE1GnliCP7I+xd0sl
# GYgYOUDJjDtCiR9hYwT47LNiuOcEo0HjosVUeeB2XXn7CwTJ1/NiSzeUJQ6NQP8r
# DTIX7EgAd7w6AM1VamrAiOa/9HcYKtVrFXI2sG+fa4M66lqjMELX+KbSGcxWDKUC
# AwEAAaOCARswggEXMB0GA1UdDgQWBBS8OgvMEaMTpYzh66JvyRaMO5iBazAfBgNV
# HSMEGDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8ETzBNMEugSaBHhkVo
# dHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNUaW1T
# dGFQQ0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUFBzAC
# hj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1RpbVN0YVBD
# QV8yMDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsGAQUF
# BwMIMA0GCSqGSIb3DQEBCwUAA4IBAQCfsvpgDi6ueE2Nlg4gpGBDnQFAmZhPo45r
# so+R0LqpRn4zonl44FXcmJARMN0r3iDw9subb0N/D0nwUbGHWqasQ6wG8DJRYTBx
# l6Vcr1yBuGhBFWT1PxS8MNG0tXmpBfeZiDBbS/2IdZRtTVDV6vMifHeeKOsIRRXL
# v8tgb0vPtCxxVNEkzYkfPGeR847w88iqaHLd9ofhG3T8Ft/c/VVOHQDDa+aXHJJo
# gO+71nKRQXYdU/tLhr4Cqpkh7xQNlsyGZCaNuoMwCKFRqRP+kkW1FqORHEhJa4AL
# 6ZYNCiHpUIQaRfuVIw11fM7j7TOQU94hvvOfzk0lLk8IRb6i6KQiMIIGcTCCBFmg
# AwIBAgIKYQmBKgAAAAAAAjANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMx
# EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT
# FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3Qg
# Q2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMTAwNzAxMjEzNjU1WhcNMjUw
# NzAxMjE0NjU1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQ
# MA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u
# MSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCASIwDQYJ
# KoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkdDbx3EYo6IOz8E5f1+n9plGt0VBDV
# pQoAgoX77XxoSyxfxcPlYcJ2tz5mK1vwFVMnBDEfQRsalR3OCROOfGEwWbEwRA/x
# YIiEVEMM1024OAizQt2TrNZzMFcmgqNFDdDq9UeBzb8kYDJYYEbyWEeGMoQedGFn
# kV+BVLHPk0ySwcSmXdFhE24oxhr5hoC732H8RsEnHSRnEnIaIYqvS2SJUGKxXf13
# Hz3wV3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v0Ev9buWayrGo8noqCjHw2k4GkbaI
# CDXoeByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN/LzAyURdXhacAQVPIk0CAwEAAaOC
# AeYwggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTVYzpcijGQ80N7fEYb
# xTNoWoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYw
# DwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvXzpoY
# xDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtp
# L2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYIKwYB
# BQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20v
# cGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDCBoAYDVR0gAQH/
# BIGVMIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6Ly93d3cu
# bWljcm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYIKwYBBQUH
# AgIwNB4yIB0ATABlAGcAYQBsAF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0AGUAbQBl
# AG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAAfmiFEN4sbgmD+BcQM9naOhIW+z
# 66bM9TG+zwXiqf76V20ZMLPCxWbJat/15/B4vceoniXj+bzta1RXCCtRgkQS+7lT
# jMz0YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3TvQhDIr79/xn/yN31aPxzymXlKkVIA
# rzgPF/UveYFl2am1a+THzvbKegBvSzBEJCI8z+0DpZaPWSm8tv0E4XCfMkon/VWv
# L/625Y4zu2JfmttXQOnxzplmkIz/amJ/3cVKC5Em4jnsGUpxY517IW3DnKOiPPp/
# fZZqkHimbdLhnPkd/DjYlPTGpQqWhqS9nhquBEKDuLWAmyI4ILUl5WTs9/S/fmNZ
# JQ96LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF0M2n0O99g/DhO3EJ3110mCIIYdqw
# UB5vvfHhAN/nMQekkzr3ZUd46PioSKv33nJ+YWtvd6mBy6cJrDm77MbL2IK0cs0d
# 9LiFAR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7aKLix
# qduWsqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQcdeh
# 0sVV42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+NR4I
# uto229Nfj950iEkSoYIC1zCCAkACAQEwggEAoYHYpIHVMIHSMQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJl
# bGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJjAkBgNVBAsTHVRoYWxlcyBUU1MgRVNO
# OkEyNDAtNEI4Mi0xMzBFMSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBT
# ZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQBO1zQhf9bHJcmAoCSPgmjIAjoiGKCBgzCB
# gKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQH
# EwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNV
# BAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMA0GCSqGSIb3DQEBBQUA
# AgUA43O29zAiGA8yMDIwMTIwNDAzMTE1MVoYDzIwMjAxMjA1MDMxMTUxWjB3MD0G
# CisGAQQBhFkKBAExLzAtMAoCBQDjc7b3AgEAMAoCAQACAiOjAgH/MAcCAQACAhHE
# MAoCBQDjdQh3AgEAMDYGCisGAQQBhFkKBAIxKDAmMAwGCisGAQQBhFkKAwKgCjAI
# AgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJKoZIhvcNAQEFBQADgYEAbHN0Z0qkLQ3K
# Fg8gcR/I1q7QPYCBfbmjm8+4G6oY0bBRptZ78G883g6tKqpTgPha4w0XLkQnRTD2
# zJKgcWhzSSu5r+XQxcdqk28VyO0BOt26ETK53chSv7srJUfThZY1d9ebpPj9LBD4
# 3jFUFuMu5eH3WeNyjiolRCB3r1i+fwYxggMNMIIDCQIBATCBkzB8MQswCQYDVQQG
# EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG
# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQg
# VGltZS1TdGFtcCBQQ0EgMjAxMAITMwAAAT/hfu2j2P+/2QAAAAABPzANBglghkgB
# ZQMEAgEFAKCCAUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8GCSqGSIb3
# DQEJBDEiBCCyKi1Zg8blS4/x3+GhWcyfPo/MxXnkS9DiHyE7DGbooDCB+gYLKoZI
# hvcNAQkQAi8xgeowgecwgeQwgb0EIEQ7Wa+cFXjB3Kah2rWUmT7Mm8Jq+UIIjLxb
# PiMZKUsgMIGYMIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0
# b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3Jh
# dGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMA
# AAE/4X7to9j/v9kAAAAAAT8wIgQg89MAUfqMZXmFfnG17nZlrNMOvEKdY567Ag7+
# MuGm4xgwDQYJKoZIhvcNAQELBQAEggEAHKWxukW/X0gmvR+PxHcz86pK7izuk3w/
# +O5wg5QjerqQBHr+CWg7SlPS/ILhmRCSt4Hz6/CsVKToEWJASSbfQDzGL3C0WorH
# 4ZPw46jCwfcrt6N5s9FS3ugKtXaDp1n8VOZ4oe3yO4SFaecfWEjcHMycRBlyRb94
# hIZKId6Gw2nCMxlfwPtWkBS7OAPWGOniKTJAC9Yrp13lAzLjzyD//lQFfxzofW1t
# q/trRjY6rioxy0JnjwWByv5c/vnFHiTqaM3IwDTu8b7OnwyCO2abhbctZUOnqEd8
# FxYxzPKLgh04h0wz/BYuv4n146D5tsdzreoIwV+yK1TMn8w9bnBOKg==
# SIG # End signature block