Engines/Terraform/Invoke-AvmTerraformTransform.ps1
|
function Resolve-AvmMapotfConfigDir { <# .SYNOPSIS Resolve a scoped directory holding vendored mapotf configs. .DESCRIPTION Returns the absolute path to the '*.mptf.hcl' bundle passed to 'mapotf transform --mptf-dir'. Resolution order: 1. $env:AVM_MPTF_CONFIG_DIR/<profile>. 2. <Root>/config/mapotf/<profile>. 3. <ModuleRoot>/Resources/mapotf/<profile>. Each candidate must be a directory containing at least one '*.mptf.hcl' file. Throws AvmConfigurationException when none resolve, so the transform engine surfaces as 'skipped' (a deliberate placeholder) rather than running mapotf against an empty config set. .PARAMETER Root The consumer repository root, used to locate an optional 'config/mapotf/<profile>' override. Pass $Context.Root. .PARAMETER Profile Config profile to resolve: common, module, root, or example. .PARAMETER Optional Return $null instead of throwing when the profile does not exist. .OUTPUTS [string] absolute path to the resolved config directory. #> [CmdletBinding()] [OutputType([string])] param( [Parameter(Mandatory)] [string] $Root, [Parameter(Mandatory)] [ValidateSet('common', 'module', 'root', 'example')] [string] $Profile, [switch] $Optional ) Set-StrictMode -Version 3.0 $ErrorActionPreference = 'Stop' $candidates = New-Object System.Collections.Generic.List[string] if ($env:AVM_MPTF_CONFIG_DIR) { $candidates.Add((Join-Path $env:AVM_MPTF_CONFIG_DIR $Profile)) } $candidates.Add((Join-Path $Root (Join-Path 'config' (Join-Path 'mapotf' $Profile)))) $moduleRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) $candidates.Add((Join-Path $moduleRoot (Join-Path 'Resources' (Join-Path 'mapotf' $Profile)))) foreach ($candidate in $candidates) { if (-not $candidate) { continue } if (-not (Test-Path -LiteralPath $candidate -PathType Container)) { continue } $configs = @(Get-ChildItem -LiteralPath $candidate -Filter '*.mptf.hcl' -File -ErrorAction SilentlyContinue) if ($configs.Count -gt 0) { return (Resolve-Path -LiteralPath $candidate).ProviderPath } } if ($Optional) { return $null } throw [AvmConfigurationException]::new( ("Cannot resolve the mapotf '{0}' config profile (looked in: {1}). " -f $Profile, ($candidates -join '; ')) + ("The profile normally ships inside the module under Resources/mapotf/{0}; " -f $Profile) + ("set AVM_MPTF_CONFIG_DIR or add config/mapotf/{0}/*.mptf.hcl to override it." -f $Profile)) } function Get-AvmTerraformFile { <# .SYNOPSIS Enumerate the '*.tf' files mapotf would touch under a module root. .DESCRIPTION Returns FileInfo records for every '*.tf' file beneath $Root, excluding any path segment that begins with '.' (e.g. '.terraform', '.git') or equals 'node_modules'. Used by Invoke-AvmTerraformTransform to snapshot file hashes before/after the transform so the engine can report which files mapotf changed. Always returns an array (empty when nothing matches) so callers can rely on '.Count'. .PARAMETER Root The module root to walk. .OUTPUTS [object[]] of System.IO.FileInfo. #> [CmdletBinding()] [OutputType([object[]])] param( [Parameter(Mandatory)] [string] $Root ) Set-StrictMode -Version 3.0 $ErrorActionPreference = 'Stop' return @( Get-ChildItem -LiteralPath $Root -Recurse -File -Filter '*.tf' -ErrorAction SilentlyContinue | Where-Object { $rel = [System.IO.Path]::GetRelativePath($Root, $_.FullName) $parts = $rel -split '[\\/]' -not ($parts | Where-Object { $_.StartsWith('.') -or $_ -eq 'node_modules' }) } ) } function Get-AvmTerraformTransformTarget { [CmdletBinding()] [OutputType([pscustomobject[]])] param( [Parameter(Mandatory)] [string] $Root ) Set-StrictMode -Version 3.0 $ErrorActionPreference = 'Stop' $targets = New-Object System.Collections.Generic.List[object] $targets.Add([pscustomobject]@{ Path = $Root Scope = 'root' Profiles = @('root', 'module', 'common') }) $modulesDir = Join-Path $Root 'modules' if (Test-Path -LiteralPath $modulesDir -PathType Container) { $moduleRoots = Get-ChildItem -LiteralPath $modulesDir -Recurse -File -Filter 'terraform.tf' -ErrorAction SilentlyContinue | ForEach-Object { $_.Directory.FullName } | Sort-Object -Unique foreach ($moduleRoot in $moduleRoots) { $targets.Add([pscustomobject]@{ Path = $moduleRoot Scope = 'module' Profiles = @('module', 'common') }) } } $examplesDir = Join-Path $Root 'examples' if (Test-Path -LiteralPath $examplesDir -PathType Container) { foreach ($example in Get-ChildItem -LiteralPath $examplesDir -Directory -ErrorAction SilentlyContinue | Sort-Object Name) { $terraformFiles = @(Get-ChildItem -LiteralPath $example.FullName -File -Filter '*.tf' -ErrorAction SilentlyContinue) if ($terraformFiles.Count -eq 0) { continue } $targets.Add([pscustomobject]@{ Path = $example.FullName Scope = 'example' Profiles = @('common', 'example') }) } } return $targets.ToArray() } function Test-AvmMapotfTransientProviderError { [CmdletBinding()] [OutputType([bool])] param( [AllowEmptyString()] [string] $Output ) Set-StrictMode -Version 3.0 $ErrorActionPreference = 'Stop' if ([string]::IsNullOrWhiteSpace($Output)) { return $false } $normalized = $Output ` -replace '\x1B\[[0-?]*[ -/]*[@-~]', '' ` -replace '[\r\n│]+', ' ' ` -replace '\s+', ' ' $patterns = @( 'context deadline exceeded' 'Client\.Timeout exceeded while awaiting headers' 'failed to retrieve cryptographic signature for provider' '(?:provider|registry).*(?:500 Internal Server Error|502 Bad Gateway|503 Service Unavailable|504 Gateway Timeout)' ) foreach ($pattern in $patterns) { if ($normalized -match $pattern) { return $true } } return $false } function Invoke-AvmMapotfTransformTarget { [CmdletBinding()] param( [Parameter(Mandatory)] [object] $Target, [Parameter(Mandatory)] [object] $Options ) Set-StrictMode -Version 3.0 $ErrorActionPreference = 'Stop' $args = New-Object System.Collections.Generic.List[string] $args.Add('transform') foreach ($profile in $Target.Profiles) { $profileDir = $Options.ProfileDirs[$profile] if (-not $profileDir) { continue } $args.Add('--mptf-dir') $args.Add($profileDir) } $args.Add('--tf-dir') $args.Add($Target.Path) $stopwatch = [System.Diagnostics.Stopwatch]::StartNew() $maxRetries = 2 $attempt = 0 do { $transform = Invoke-AvmProcess ` -FilePath $Options.ToolPath ` -ArgumentList $args.ToArray() ` -WorkingDirectory $Target.Path ` -EnvVars $Options.EnvVars ` -IgnoreExitCode if ($transform.ExitCode -eq 0) { break } $combinedOutput = @($transform.StdOut, $transform.StdErr) -join [System.Environment]::NewLine if ( $attempt -ge $maxRetries -or -not (Test-AvmMapotfTransientProviderError -Output $combinedOutput) ) { $message = Add-AvmProcessFailureDetail ` -Message ('mapotf transform exited with code {0} for {1} target {2}.' -f $transform.ExitCode, $Target.Scope, $Target.Path) ` -StdOut $transform.StdOut ` -StdErr $transform.StdErr throw [AvmProcessException]::new($message) } $attempt++ $delaySeconds = $attempt * 5 Write-AvmLog ( 'transform: transient provider download failure; retrying {0} target in {1}s ({2} of {3})' -f $Target.Scope, $delaySeconds, $attempt, $maxRetries ) -Level Warning | Out-Null Start-Sleep -Seconds $delaySeconds } while ($attempt -le $maxRetries) $stopwatch.Stop() Write-AvmLog ( 'transform: {0} target completed in {1}: {2}' -f $Target.Scope, (Format-AvmDuration -Duration $stopwatch.Elapsed), $Target.Path ) -Level Verbose | Out-Null } function Invoke-AvmTerraformTransform { <# .SYNOPSIS Apply the AVM mapotf HCL transforms to a Terraform module. .DESCRIPTION Engine implementation called by Invoke-AvmTransform when the module context is Ecosystem='terraform'. Resolves the 'mapotf' binary via Resolve-AvmTool and the vendored config bundle via Resolve-AvmMapotfConfigDir, then runs, against $Context.Root: mapotf transform --mptf-dir <profile> [...] --tf-dir <target> mapotf clean-backup --tf-dir <root> Profile composition: - root: root, module, common - local module: module, common - example: common, then optional example Root-only telemetry therefore never runs against submodules or examples. Module file-layout and provider rules apply to the root and submodules. Common in-place ordering and cleanup applies everywhere. The final call removes '*.tf.mptfbackup' files. Several of the vendored configs (e.g. order_resource_attrs) read provider schemas, so mapotf shells out to 'terraform init' + 'terraform providers schema'. mapotf locates 'terraform' by name on PATH, but GitHub-hosted runners no longer ship terraform on PATH (it was removed from the images). The engine therefore resolves the pinned terraform via Resolve-AvmTool and prepends its directory to PATH for the mapotf subprocess; environment variables propagate to mapotf's own terraform grandchild, so the schema reads succeed against the managed binary. A terraform that cannot be resolved (AvmToolException) propagates so the chain reports 'skipped', matching missing-mapotf. File-hash snapshots taken before and after the transform populate the 'Changed' field (relative paths of every '*.tf' mapotf added, removed or modified). Drift mode (-CheckDrift, used by pr-check): mapotf has no dry-run, so the transform still runs and any 'Changed' file becomes a Status='fail' Issue. The transformed content is then rolled back, so drift mode leaves the working copy byte-identical. The contract is "a module that already ran pre-commit has nothing for mapotf to change"; a non-empty change set in CI therefore means the author did not run pre-commit, and pr-check flags it. Independent root, local-module, and example targets run through the bounded Invoke-AvmParallel scheduler. A configured TF_PLUGIN_CACHE_DIR forces serial target execution because Terraform's shared provider plugin cache is not concurrency-safe. mapotf exit codes: 0 = success. A transform failure caused by a recognized transient Terraform provider network error is retried twice with incremental delay; other failures and retry exhaustion surface as AvmProcessException. A missing mapotf binary (AvmToolException) or a missing config bundle (AvmConfigurationException) propagates so the composition chain reports the step as 'skipped' on an unconfigured workstation. .PARAMETER Context Module context produced by Get-AvmModuleContext. Must have Ecosystem='terraform'. .PARAMETER AllowPathFallback Pass through to Resolve-AvmTool. .PARAMETER CheckDrift When set, treat any file mapotf changed as a failure (Status='fail' with one Issue per changed file) instead of a silent fix. Used by the pr-check chain. .PARAMETER ThrottleLimit Maximum number of independent root, module, or example targets to transform at once. Defaults to one for direct engine calls. .OUTPUTS pscustomobject with Engine, Tool, ToolPath, ToolSource, Status, FilesProcessed, Changed, Issues. #> [CmdletBinding(SupportsShouldProcess)] [OutputType([pscustomobject])] param( [Parameter(Mandatory)] $Context, [switch] $AllowPathFallback, [switch] $CheckDrift, [ValidateRange(1, 32)] [int] $ThrottleLimit = 1 ) Set-StrictMode -Version 3.0 $ErrorActionPreference = 'Stop' if ($Context.Ecosystem -ne 'terraform') { throw [System.ArgumentException]::new( "Invoke-AvmTerraformTransform requires a terraform context (got Ecosystem='$($Context.Ecosystem)').") } $tool = Resolve-AvmTool -Name 'mapotf' -AllowPathFallback:$AllowPathFallback $profileDirs = @{ common = Resolve-AvmMapotfConfigDir -Root $Context.Root -Profile 'common' module = Resolve-AvmMapotfConfigDir -Root $Context.Root -Profile 'module' root = Resolve-AvmMapotfConfigDir -Root $Context.Root -Profile 'root' example = Resolve-AvmMapotfConfigDir -Root $Context.Root -Profile 'example' -Optional } $targets = @(Get-AvmTerraformTransformTarget -Root $Context.Root) Write-AvmLog ("transform: discovered {0} target(s)" -f $targets.Count) -Level Verbose | Out-Null $beforeFiles = Get-AvmTerraformFile -Root $Context.Root Write-AvmLog ("transform: discovered {0} terraform file(s)" -f $beforeFiles.Count) -Level Verbose | Out-Null if (-not $PSCmdlet.ShouldProcess($Context.Root, ("mapotf transform across {0} scoped target(s)" -f $targets.Count))) { return [pscustomobject][ordered]@{ Engine = 'terraform' Tool = ('{0}/{1}' -f $tool.Name, $tool.Version) ToolPath = $tool.Path ToolSource = $tool.Source Status = 'skipped' FilesProcessed = $beforeFiles.Count Changed = @() Issues = @() } } $before = @{} foreach ($f in $beforeFiles) { $before[$f.FullName] = (Get-FileHash -LiteralPath $f.FullName -Algorithm SHA256).Hash } # Drift mode must not mutate the caller's working copy. mapotf has no # dry-run, so snapshot every '*.tf' up front and roll back in the finally - # drift is still computed from the post-transform tree, but the tree is left # byte-identical even if mapotf throws part-way through. $snapshot = $null if ($CheckDrift) { Write-AvmLog ("transform: check-drift mode; snapshotting {0} file(s)" -f $beforeFiles.Count) -Level Verbose | Out-Null $snapshot = Get-AvmFileSnapshot -Path @($beforeFiles | ForEach-Object { $_.FullName }) } try { # mapotf reads provider schemas (order_resource_attrs et al.) by shelling # out to terraform, which it finds by name on PATH. GitHub-hosted runners # no longer ship terraform on PATH, so resolve the pinned terraform the # same way as mapotf (managed cache, not a stray PATH binary) and prepend # its directory to PATH for the mapotf subprocess. The override propagates # to mapotf's terraform grandchild. A missing terraform throws # AvmToolException, which the chain surfaces as 'skipped' just like a # missing mapotf binary. $terraform = Resolve-AvmTool -Name 'terraform' -AllowPathFallback:$AllowPathFallback Write-AvmLog ("transform: resolved terraform dependency at {0}" -f $terraform.Path) -Level Verbose | Out-Null $mapotfEnv = New-AvmToolPathEnvironment ` -ToolPath $terraform.Path ` -ToolName 'terraform' $effectiveThrottle = $ThrottleLimit $pluginCache = [string]$env:TF_PLUGIN_CACHE_DIR if ($effectiveThrottle -gt 1 -and -not [string]::IsNullOrWhiteSpace($pluginCache)) { $effectiveThrottle = 1 Write-AvmLog ( 'transform: TF_PLUGIN_CACHE_DIR is configured; running Mapotf targets serially because the shared Terraform provider cache is not concurrency-safe' ) -Level Verbose | Out-Null } $transformOptions = [pscustomobject]@{ ToolPath = $tool.Path ProfileDirs = $profileDirs EnvVars = $mapotfEnv } Invoke-AvmParallel ` -InputObject $targets ` -FunctionName 'Invoke-AvmMapotfTransformTarget' ` -Argument $transformOptions ` -ThrottleLimit $effectiveThrottle Write-AvmLog 'transform: mapotf scoped transforms completed' -Level Verbose | Out-Null foreach ($target in $targets) { $clean = Invoke-AvmProcess ` -FilePath $tool.Path ` -ArgumentList @('clean-backup', '--tf-dir', $target.Path) ` -WorkingDirectory $target.Path ` -EnvVars $mapotfEnv ` -IgnoreExitCode if ($clean.ExitCode -ne 0) { $message = Add-AvmProcessFailureDetail ` -Message ('mapotf clean-backup exited with code {0} for {1} target {2}.' -f $clean.ExitCode, $target.Scope, $target.Path) ` -StdOut $clean.StdOut ` -StdErr $clean.StdErr throw [AvmProcessException]::new( $message) } } Write-AvmLog 'transform: mapotf clean-backup completed' -Level Verbose | Out-Null $afterFiles = Get-AvmTerraformFile -Root $Context.Root $seen = New-Object 'System.Collections.Generic.HashSet[string]' $changed = New-Object System.Collections.Generic.List[string] foreach ($f in $afterFiles) { $null = $seen.Add($f.FullName) $rel = [System.IO.Path]::GetRelativePath($Context.Root, $f.FullName) $hash = (Get-FileHash -LiteralPath $f.FullName -Algorithm SHA256).Hash if (-not $before.ContainsKey($f.FullName)) { $changed.Add($rel) } elseif ($before[$f.FullName] -ne $hash) { $changed.Add($rel) } } foreach ($key in $before.Keys) { if (-not $seen.Contains($key)) { $changed.Add([System.IO.Path]::GetRelativePath($Context.Root, $key)) } } } finally { if ($null -ne $snapshot) { Write-AvmLog 'transform: restoring terraform snapshot after drift check' -Level Verbose | Out-Null $current = @(Get-AvmTerraformFile -Root $Context.Root | ForEach-Object { $_.FullName }) Restore-AvmFileSnapshot -Snapshot $snapshot -CurrentPath $current } } $status = 'pass' $issues = New-Object System.Collections.Generic.List[object] if ($CheckDrift -and $changed.Count -gt 0) { $status = 'fail' foreach ($rel in $changed) { $issues.Add([pscustomobject][ordered]@{ File = $rel Line = 0 Column = 0 Severity = 'error' Code = 'avm.tf.mapotf-drift' Message = ("mapotf transform modified '{0}'; run 'avm pre-commit -Ecosystem terraform' and commit the result." -f $rel) }) } Write-AvmLog ("transform: completed; processed={0}; changed={1}; status={2}" -f $beforeFiles.Count, $changed.Count, $status) -Level Verbose | Out-Null } return [pscustomobject][ordered]@{ Engine = 'terraform' Tool = ('{0}/{1}' -f $tool.Name, $tool.Version) ToolPath = $tool.Path ToolSource = $tool.Source Status = $status FilesProcessed = $beforeFiles.Count Changed = $changed.ToArray() Issues = $issues.ToArray() } } # SIG # Begin signature block # MIInKwYJKoZIhvcNAQcCoIInHDCCJxgCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAzm6/OUMVDvIMW # YFvx2RTsNcMmqnTgejh6TKiWiMc4wqCCDLowggX1MIID3aADAgECAhMzAAACHU0Z # yE7XD1dIAAAAAAIdMA0GCSqGSIb3DQEBCwUAMFcxCzAJBgNVBAYTAlVTMR4wHAYD # VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBD # b2RlIFNpZ25pbmcgUENBIDIwMjQwHhcNMjYwNDE2MTg1OTQzWhcNMjcwNDE1MTg1 # OTQzWjB0MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE # BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYD # VQQDExVNaWNyb3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IB # DwAwggEKAoIBAQDQvewXxx9gZZFC6Ys1WBay8BJ8kGA4JQnH5CMafqOASlTpK9H8 # o5ZXTXt0caVQTNMUPt445wXYD+dFtaKWTwDn1I52oUSrC9vJin1Gsqt+zyKJL5Dg # 3eQXbQNR61DmMy20GLTIO3SFed9Rfi/ophgCLGFLDR3r0KvHjwMb/jYWS0celV/4 # Lz27LfAekm8v9E5IXaeiXbAUYZKK090n4CVl3JBtbN+9DtI9SNu/yjvozW52/u7R # X/Ttpa/KDlpuokZ+Zcbvmtd9ur9gFLvZzh41o9MsE/clQtdaFWGvuo6Jua/ntpgk # ey3E5/vBFe+MJPG6phdnuo6r57ZudCudiI1bAgMBAAGjggGbMIIBlzAOBgNVHQ8B # Af8EBAMCB4AwHwYDVR0lBBgwFgYKKwYBBAGCN0wIAQYIKwYBBQUHAwMwHQYDVR0O # BBYEFH6QuMwqcPG0hQlQ6c5jCtTTLrVeMEUGA1UdEQQ+MDykOjA4MR4wHAYDVQQL # ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xFjAUBgNVBAUTDTIzMDAxMis1MDc1NTkw # HwYDVR0jBBgwFoAUf1k/VCHarU/vBeXmo9ctBpQSCDEwYAYDVR0fBFkwVzBVoFOg # UYZPaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9jcmwvTWljcm9zb2Z0 # JTIwQ29kZSUyMFNpZ25pbmclMjBQQ0ElMjAyMDI0LmNybDBtBggrBgEFBQcBAQRh # MF8wXQYIKwYBBQUHMAKGUWh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMv # Y2VydHMvTWljcm9zb2Z0JTIwQ29kZSUyMFNpZ25pbmclMjBQQ0ElMjAyMDI0LmNy # dDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4ICAQBKTbYOjzwTG/DXGaz9 # s6+fQeaTtDcFmMY+5UyVFCyj7Pv+5i37qfX8lSL/tBIfYQfWsMuBQlfZurJD6r4H # VJ2CeH+1fgiq8dcHdVKoZ3Sa2qXoX3cq9iS8cVb06B7+5/XJ7I0OxHH9fDsvJ3T3 # w5V/ZtAIFmLrl+P0CtG+92uzRsn0nTbdFjOkLMLWPLAU3THohKRlSEMgFJpPkm5n # 5UAZ35xX6FWCrDLsSKb555bTifwa8mJBwdlof0bmfYidH+dxZ1FdDxvLnNl9zeKs # A4kejaaIqqIPguhwAti5Ql7BlTNoJNwxCvBmqW2MQLnCkYN/VVUsR3V2x/rcTNzo # Bf/Z/SpROvdaA2ZOOd1uioXJt3tdLQ7vHpqpib0KfWr/FWXW10q38VxfCnRQBqzb # SuztR7nEMuzX7Ck+B/XaPDXd1qh72+QYyB0Z2VzWmO9zsnb9Uq/dwu8LGeQqnyu6 # 7SDGACvnXii2fb9+US492VTnXSnFKyqwgzUyFMtZK1/sHYTv6bG4TtQUygQxTN+Z # V+aJIlKO2MqZ7bKrAnOzS9m6NgoTdWOq11bTOZwKlIEV/EhV9SWkDmdpR/hPPT2v # 6TEj4F8PT/zHjRezIU5c/DGlt/VhY/pK0XkJtEyMmmS1BMtjU/rqBZVMIm3dnxQs # /TBByr+Cf8Z1r7aifQVQ+WSqzjCCBr0wggSloAMCAQICEzMAAAA5O7Y3Gb8GHWcA # AAAAADkwDQYJKoZIhvcNAQEMBQAwgYgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpX # YXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQg # Q29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29mdCBSb290IENlcnRpZmljYXRl # IEF1dGhvcml0eSAyMDExMB4XDTI0MDgwODIwNTQxOFoXDTM2MDMyMjIyMTMwNFow # VzELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEo # MCYGA1UEAxMfTWljcm9zb2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAyNDCCAiIwDQYJ # KoZIhvcNAQEBBQADggIPADCCAgoCggIBANgBnB7jOMeqlRYHNa265v4IY9fH8TKh # emHfPINe1gpLaV3dhg324WwH06LcHbpnsBukCDNitryo0dtS/EW6I/yEL/bLSY8h # KpbfQuWusBPr9qazYcDxCW/qnjb5JsI1s8bNOg3bVATvQVL4tcf03aTycsz8QeCd # M0l/yHRObJ9QqazM1r6VPEOJ7LL+uEEb73w6QCuhs89a1uv1zerOYMnsneRRwCbp # yW11IcggU0cRKDDq1pjVJzIbIF6+oiXXbReOsgeI8zu1FyQfK0fVkaya8SmVHQ/t # Of23mZ4W9k0Ri22QW9p3UgSC5OUDktKxxcCmGL6tXLfOGSWHIIV4YrTJTT6PNty5 # REojHJuZHArkF9VnHTERWoTjAzfI3kP+5b4alUdhgAZ7ttOu1bVnXfHaqPYl2rPs # 20ji03LOVWsh/radgE17es5hL+t6lV0eVHrVhsssROWJuz2MXMCt7iw7lFPG9LXK # Gjsmonn2gotGdHIuEg5JnJMJVmixd5LRlkmgYRZKzhxSCwyoGIq0PhaA7Y+VPct5 # pCHkijcIIDm0nlkK+0KyepolcqGm0T/GYQRMhHJlGOOmVQop36wUVUYklUy++vDW # eEgEo4s7hxN6mIbf2MSIQ/iIfMZgJxC69oukMUXCrOC3SkE/xIkgpfl22MM1itkZ # 35nNXkMolU1lAgMBAAGjggFOMIIBSjAOBgNVHQ8BAf8EBAMCAYYwEAYJKwYBBAGC # NxUBBAMCAQAwHQYDVR0OBBYEFH9ZP1Qh2q1P7wXl5qPXLQaUEggxMBkGCSsGAQQB # gjcUAgQMHgoAUwB1AGIAQwBBMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU # ci06AjGQQ7kUBU7h6qfHMdEjiTQwWgYDVR0fBFMwUTBPoE2gS4ZJaHR0cDovL2Ny # bC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0MjAx # MV8yMDExXzAzXzIyLmNybDBeBggrBgEFBQcBAQRSMFAwTgYIKwYBBQUHMAKGQmh0 # dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljUm9vQ2VyQXV0MjAx # MV8yMDExXzAzXzIyLmNydDANBgkqhkiG9w0BAQwFAAOCAgEAFJQfOChP7onn6fLI # MKrSlN1WYKwDFgAddymOUO3FrM8d7B/W/iQ6DxXsDn7D5W4wMwYeLystcEqfkjz4 # NURRgazyMu5yRzQh4LqjA4tStTcJh1opExo7nn5PuPBYnbu0+THSuVHTe0VTTPVh # ily/piFrDo3axQ9P4C+Ol5yet+2gTfekICS5xS+cYfSIvgn0JksVBVMYVI5QFu/q # hnLhsEFEUzG8fvv0hjgkO+lkpV9ty6GkN4vdnd7ya6Q6aR9y34aiM1qmxaxBi6OU # nyNl6fkuun/diTFnYDLTppOkr/mg5WSfCiDVMNCxtj4wPKC5OmHm1DQIt/MNokbb # H3UGsFP1QbzsLocuSqLCvH09Io3fDPTmscR9Y75G4qX7RTX8AdBPo0I6OEojf39z # uFZt0qOHm65YWQE69cZM2ueE1MB05dNNgHK9gTE7zKvK/fg8B2qjW88MT/WF5V5u # vZGtqa9FSL2RazArA+rDPuf6JGYz4HpgMZHB4S6szWSKYBv0VisCzfxgeU+dquXW # 9bd0auYlOB58DPcOYKdc3Se94g+xL4pcEhbB54JOgAkwYTu/9dLeH2pDqeJZAABV # DWRQCaXfO5LgyKwKCLYXpigrZYCjUSBcr+Ve8PFWMhVTQl0v4q8J/AUmQN5W4n10 # 1cY2L4A7GTQG1h32HHAvfQESWP0xghnHMIIZwwIBATBuMFcxCzAJBgNVBAYTAlVT # MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jv # c29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMjQCEzMAAAIdTRnITtcPV0gAAAAAAh0w # DQYJYIZIAWUDBAIBBQCggZAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwLwYJ # KoZIhvcNAQkEMSIEIE7tInRQZ/us4qEJ+iSSJYb3zeqystLNzRpmDsPLEH82MEIG # CisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8AcwBvAGYAdKEagBhodHRwOi8v # d3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEBBQAEggEAwMppDKG9BDPydeOm # u6zym5hT1BcSE5FP2S94UXcBkliBZ7LXSMOET4ApXeBktKb1PPNrF/fAXWuGrN0+ # MzNswdU7fmus9P7+8F4Ok08WPzC8XcTDITeISvdL4+moiAOCj821NagqPexpSAWP # Lm7wxVKttR3hLqvPjHQKlqIqc4wQeZCbtJlyviZ+o9j078bU0FbhJCBm+1ychtXY # t340LDZGB/U52n9dtToTrdFgok2QIMs42ygGNVG6Id4XgZjtTicWpsnuq6U/l6jG # CSb2oGi9BW1xoXzDLhoE+RV91Rh2YY1eKWXprq/p7pQL0hu1+wrzIgsk0XiPMGKd # G3bB76GCF5cwgheTBgorBgEEAYI3AwMBMYIXgzCCF38GCSqGSIb3DQEHAqCCF3Aw # ghdsAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFSBgsqhkiG9w0BCRABBKCCAUEEggE9 # MIIBOQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFlAwQCAQUABCC4xxPFxzIciZT9 # x6mOVWUwZfZcld7sAkO2FxxUo4sedAIGaoVaWu+vGBMyMDI2MDkwNDE0MDEwNi40 # NjlaMASAAgH0oIHRpIHOMIHLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGlu # Z3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv # cmF0aW9uMSUwIwYDVQQLExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25zMScw # JQYDVQQLEx5uU2hpZWxkIFRTUyBFU046OTYwMC0wNUUwLUQ5NDcxJTAjBgNVBAMT # HE1pY3Jvc29mdCBUaW1lLVN0YW1wIFNlcnZpY2WgghHtMIIHIDCCBQigAwIBAgIT # MwAAAiY1tD5nQ5P2HwABAAACJjANBgkqhkiG9w0BAQsFADB8MQswCQYDVQQGEwJV # UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE # ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGlt # ZS1TdGFtcCBQQ0EgMjAxMDAeFw0yNjAyMTkxOTQwMDJaFw0yNzA1MTcxOTQwMDJa # MIHLMQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH # UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSUwIwYDVQQL # ExxNaWNyb3NvZnQgQW1lcmljYSBPcGVyYXRpb25zMScwJQYDVQQLEx5uU2hpZWxk # IFRTUyBFU046OTYwMC0wNUUwLUQ5NDcxJTAjBgNVBAMTHE1pY3Jvc29mdCBUaW1l # LVN0YW1wIFNlcnZpY2UwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC/ # /w+ZZIL5RFFpVI8D3ZyuNu8IzcAEOD30OLYjh337rXjcrIlOSzpJc4ZeUxEyli6x # 6F6zm4NR8dbPb9diDp/hOUzHWGxiA1Z3RXKBb/4F/ojyvN43SEGWqSfVc3I3BlsY # T35ecVAJ9kVf90YOv29tFjJBBZkYvrT/DwwyRLscOyP4p+9/lyJjD+ULs3YXBhVr # fZ+MbQB+BYKLqRvBKbj/wR9akNrMxQINoGaD5jZO/N/nSsmG2P1zv/cv4gSoMBnW # eQIBkjd2I5w1DeXupp2vSiNmR5sA2ZkBK3yiQWaJvRxODlkfiyHk9Mkk/TrYTjmj # PCbhe+uqhHNRy8UlbOvWsCq0tRtUykHv39DgqAfJNrE8OSt835rBzDprrcAhwmgf # hoVi4AKeqwikY0nUa48K0Qy80XT4fiEA3ExEZNaRFo9Nq/GwbfgqKqGmc9xhKuRF # cjtua4KHZvnAvpWgEFSOCkovXs/BcLnkEHM9xZ8iUag5CyhNqXYYE/z0pcXdYaNI # kQ68EWmuvLm7g9oofV2vOm5GVNoghnkWG6nGPo/JwEgmA9oSS0EfvFRMWPA/gpSv # F3shArKHnaEpVSSi3DNbyiuYiEs9Ko0IkZc8xKFeQRaqGRxrB+2r/7B3X81Tps99 # KhFwg+wD87od22F2MUg1x7twt3gaVnFk0IZIwUPCGwIDAQABo4IBSTCCAUUwHQYD # VR0OBBYEFF3hn9fYJN2Y/Z9LVbBPIxAzXHsQMB8GA1UdIwQYMBaAFJ+nFV0AXmJd # g/Tl0mWnG1M1GelyMF8GA1UdHwRYMFYwVKBSoFCGTmh0dHA6Ly93d3cubWljcm9z # b2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0El # MjAyMDEwKDEpLmNybDBsBggrBgEFBQcBAQRgMF4wXAYIKwYBBQUHMAKGUGh0dHA6 # Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwVGlt # ZS1TdGFtcCUyMFBDQSUyMDIwMTAoMSkuY3J0MAwGA1UdEwEB/wQCMAAwFgYDVR0l # AQH/BAwwCgYIKwYBBQUHAwgwDgYDVR0PAQH/BAQDAgeAMA0GCSqGSIb3DQEBCwUA # A4ICAQA2Ux0tr9sYCjsq0FRyiVpx15OurNXv6Qk7iX+ArVPlz3w4tqjcTNm1dt3t # Tua2wJMpJhPH8n7UXhmT98d5Du44Ll4adnse4SQfVg3QL6aRkXHnJUn8y9iftB/P # y22n9xnwPFfj3QlDOSgLuHleu97U0iH2ZaluYabWXJihdiYpK8cPHFlqZOAiot0+ # GD8dP+RMuvpxt/F2LmYelpoZwriiFOUmlxEUV7xJHyZZlDquskeyuq01DTv91N4q # M8cfPPhl/2pc4HeMf/nd2HouifJbDQFNd4WPhLzn0Sy3u1Zh3+S3tjQdqN+dyw60 # RaV+RXCoOLgFZ3MAg/GoDl+fvb5hy/1a71ctX8wEad1Pf6def2pqfl3wFc++hkF8 # DXXTZofJN4YVaN3InwbAGQDDkNK4lqecCixxmSKwidPynGeE5OtvNoK1pkLsm/i8 # F1RjGczZ/kSF2VDkqG866iQ+jVbGOQ6Du3eyyFcFKZoDJ4B5mEAS9aT2SKqllLey # bOboH6r67siR5B/2Hnu7+KYuYZy0BEadtA6ngG4cnSR9JsrkhhsKmb11ujqwgJyN # x92MsoGGwNgN1aI0QID8CsjCFwpfmMzlA44xHKYv3hmjxeqBS4uU5rQeiAnVgpJe # aVGKm/lzPDtnppGV+7XhRp5b1ZxT/Z7Xxc+I7H7/jCtQDZoaZTCCB3EwggVZoAMC # AQICEzMAAAAVxedrngKbSZkAAAAAABUwDQYJKoZIhvcNAQELBQAwgYgxCzAJBgNV # BAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4w # HAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAwBgNVBAMTKU1pY3Jvc29m # dCBSb290IENlcnRpZmljYXRlIEF1dGhvcml0eSAyMDEwMB4XDTIxMDkzMDE4MjIy # NVoXDTMwMDkzMDE4MzIyNVowfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp # bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw # b3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTAw # ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDk4aZM57RyIQt5osvXJHm9 # DtWC0/3unAcH0qlsTnXIyjVX9gF/bErg4r25PhdgM/9cT8dm95VTcVrifkpa/rg2 # Z4VGIwy1jRPPdzLAEBjoYH1qUoNEt6aORmsHFPPFdvWGUNzBRMhxXFExN6AKOG6N # 7dcP2CZTfDlhAnrEqv1yaa8dq6z2Nr41JmTamDu6GnszrYBbfowQHJ1S/rboYiXc # ag/PXfT+jlPP1uyFVk3v3byNpOORj7I5LFGc6XBpDco2LXCOMcg1KL3jtIckw+DJ # j361VI/c+gVVmG1oO5pGve2krnopN6zL64NF50ZuyjLVwIYwXE8s4mKyzbnijYjk # lqwBSru+cakXW2dg3viSkR4dPf0gz3N9QZpGdc3EXzTdEonW/aUgfX782Z5F37Zy # L9t9X4C626p+Nuw2TPYrbqgSUei/BQOj0XOmTTd0lBw0gg/wEPK3Rxjtp+iZfD9M # 269ewvPV2HM9Q07BMzlMjgK8QmguEOqEUUbi0b1qGFphAXPKZ6Je1yh2AuIzGHLX # pyDwwvoSCtdjbwzJNmSLW6CmgyFdXzB0kZSU2LlQ+QuJYfM2BjUYhEfb3BvR/bLU # HMVr9lxSUV0S2yW6r1AFemzFER1y7435UsSFF5PAPBXbGjfHCBUYP3irRbb1Hode # 2o+eFnJpxq57t7c+auIurQIDAQABo4IB3TCCAdkwEgYJKwYBBAGCNxUBBAUCAwEA # ATAjBgkrBgEEAYI3FQIEFgQUKqdS/mTEmr6CkTxGNSnPEP8vBO4wHQYDVR0OBBYE # FJ+nFV0AXmJdg/Tl0mWnG1M1GelyMFwGA1UdIARVMFMwUQYMKwYBBAGCN0yDfQEB # MEEwPwYIKwYBBQUHAgEWM2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMv # RG9jcy9SZXBvc2l0b3J5Lmh0bTATBgNVHSUEDDAKBggrBgEFBQcDCDAZBgkrBgEE # AYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB # /zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvXzpoYxDBWBgNVHR8ETzBNMEug # SaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9N # aWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsG # AQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jv # b0NlckF1dF8yMDEwLTA2LTIzLmNydDANBgkqhkiG9w0BAQsFAAOCAgEAnVV9/Cqt # 4SwfZwExJFvhnnJL/Klv6lwUtj5OR2R4sQaTlz0xM7U518JxNj/aZGx80HU5bbsP # MeTCj/ts0aGUGCLu6WZnOlNN3Zi6th542DYunKmCVgADsAW+iehp4LoJ7nvfam++ # Kctu2D9IdQHZGN5tggz1bSNU5HhTdSRXud2f8449xvNo32X2pFaq95W2KFUn0CS9 # QKC/GbYSEhFdPSfgQJY4rPf5KYnDvBewVIVCs/wMnosZiefwC2qBwoEZQhlSdYo2 # wh3DYXMuLGt7bj8sCXgU6ZGyqVvfSaN0DLzskYDSPeZKPmY7T7uG+jIa2Zb0j/aR # AfbOxnT99kxybxCrdTDFNLB62FD+CljdQDzHVG2dY3RILLFORy3BFARxv2T5JL5z # bcqOCb2zAVdJVGTZc9d/HltEAY5aGZFrDZ+kKNxnGSgkujhLmm77IVRrakURR6nx # t67I6IleT53S0Ex2tVdUCbFpAUR+fKFhbHP+CrvsQWY9af3LwUFJfn6Tvsv4O+S3 # Fb+0zj6lMVGEvL8CwYKiexcdFYmNcP7ntdAoGokLjzbaukz5m/8K6TT4JDVnK+AN # uOaMmdbhIurwJ0I9JZTmdHRbatGePu1+oDEzfbzL6Xu/OHBE0ZDxyKs6ijoIYn/Z # cGNTTY3ugm2lBRDBcQZqELQdVTNYs6FwZvKhggNQMIICOAIBATCB+aGB0aSBzjCB # yzELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl # ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjElMCMGA1UECxMc # TWljcm9zb2Z0IEFtZXJpY2EgT3BlcmF0aW9uczEnMCUGA1UECxMeblNoaWVsZCBU # U1MgRVNOOjk2MDAtMDVFMC1EOTQ3MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1T # dGFtcCBTZXJ2aWNloiMKAQEwBwYFKw4DAhoDFQCi/fMxFtkqr7XMXdsRyWU0lSKH # Z6CBgzCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw # DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x # JjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMA0GCSqGSIb3 # DQEBCwUAAgUA7kTuKjAiGA8yMDI2MDkwNDA3MTM0NloYDzIwMjYwOTA1MDcxMzQ2 # WjB3MD0GCisGAQQBhFkKBAExLzAtMAoCBQDuRO4qAgEAMAoCAQACAi+FAgH/MAcC # AQACAhLvMAoCBQDuRj+qAgEAMDYGCisGAQQBhFkKBAIxKDAmMAwGCisGAQQBhFkK # AwKgCjAIAgEAAgMHoSChCjAIAgEAAgMBhqAwDQYJKoZIhvcNAQELBQADggEBACRl # QolrlMTtJFsSlJkRw+SeQTBg58sBjnAVerBOk8Kt8g06WMkTLnPZE0hs0pNjsf37 # 1VB9tej7KmS26w8ow9epjcY12TnlKjMY1UCwqnrtRMzxzUYUNjK4EprJ/gNTNBQ7 # panDETKHo/q2Rn5zK658ulSoUqbKZzJ60JDX656gkUTvVYd4KTO+L1GVFAhTsC8N # E7YF57iE9QNrhc9qVsev9IFJHLkUUs2kLGU4l8zbglgpfvkR2ZGmDc3DZtbyIXdZ # 1kr00pcWnMewBYR99hGyV1TWMD7B1gNbNxdDRptF/osR1wrvFl+UBBqlpIWuyV16 # MvGi7hivzdMqi520PSQxggQNMIIECQIBATCBkzB8MQswCQYDVQQGEwJVUzETMBEG # A1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWlj # cm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFt # cCBQQ0EgMjAxMAITMwAAAiY1tD5nQ5P2HwABAAACJjANBglghkgBZQMEAgEFAKCC # AUowGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMC8GCSqGSIb3DQEJBDEiBCDS # gxlGEMBMgmbrqU7RddcZcbzGBUmBL8Fa/hIkC3kRTzCB+gYLKoZIhvcNAQkQAi8x # geowgecwgeQwgb0EIMwyXGFnTNsZRBrs6GN/BbV0okaNP3VBYqLFjUsFnbgqMIGY # MIGApH4wfDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNV # BAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQG # A1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENBIDIwMTACEzMAAAImNbQ+Z0OT # 9h8AAQAAAiYwIgQgi+Og7yRbNb89rFwwJsbuyG/BxvCcLNWRFC4h7axrO38wDQYJ # KoZIhvcNAQELBQAEggIALJKLOvgUT3/OPikMBC/yCeOa6t/tNSxPWdzp/gQbfPmV # c4fx28i2G6sX8mepr/apWMeX53Nvyl+j4VHnf7jwznf5waThqDscwI+MST4yabtP # PL+bZOqf0+F+A15bKKwPeqbvfAr/0IBb0iob9Qyb86ZOpPC7/l6Cdqhqc/6KYR/3 # +GNEIGzI/ccPaAqNASQCwICA0DwBd4TtARafuGD/WAyhm+MeIqsb9kpiptsFha/S # nr/BSb5G2PpLAnnhRHD4JkwaGNvPUbZWSoIe7GEi6knCdiRzaJ8vsk86rFkYv9HI # fqUBJEBh0TF79iYpI3rghrF6ZB7UgrcNZccZxZ0vRoQl97ykpEhOll4MrtqRXbQq # v5uFz7xrBxHE2GxO/tG1/yUy3FhSyoEM9VAn7XDGri54CWAJBDbnF4Eoo7mwkiSY # 6eQHW3IQO7KZJSa/AjMmBlwHQ9JSgiTIzzCFcVKZwlOYmZ7WDGGd8K51PIUYbGuc # zVD0Tmvc/M1kngXKXlAjHxonunuq93u2q023sJjh8f3iV95mn5VY4oSWExWJYzXy # ergcMhVLqCvPe49pLEovj+3aR6KvuiTQfxaXbrK2spEnuolJZvnWP/HDos+8Fzyw # W632IoSbNvgi5qeYSvFMtDEZHBhWoHAGAhQlFczdPzW4lpoizhrOi75TmYPuaDI= # SIG # End signature block |