CI: Add support for GHC 8.10, stack and live-server testing (#156)

* CI: Add support for GHC 8.10, stack and live-server testing

* Fix live-server tests for all platforms

* Fix windows tests

* Fix resourcet cleanup exceptions

* Mark minio-hs builds GHC 8.4, 8.8 on windows experimental

* Use minio with erasure code backend for tests

* Fix matrix combinations for cabal and stack

Co-authored-by: Krishnan Parthasarathi <kp@minio.io>
This commit is contained in:
Aditya Manthramurthy 2020-06-24 10:35:11 -07:00 committed by GitHub
parent 3dd235a1ad
commit a3538aa46c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 318 additions and 95 deletions

119
.github/workflows/haskell-cabal.yml vendored Normal file
View File

@ -0,0 +1,119 @@
name: Haskell CI (Cabal)
on:
schedule:
# Run every weekday
- cron: '0 0 * * 1-5'
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
cabal-build:
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.experimental }}
strategy:
fail-fast: false
matrix:
ghc: ['8.4', '8.6', '8.8', '8.10']
cabal: ['3.2']
os: [ubuntu-latest, macOS-latest]
experimental: [false]
include:
- ghc: '8.6'
cabal: '3.2'
os: windows-latest
experimental: false
- ghc: '8.10'
cabal: '3.2'
os: windows-latest
experimental: false
# Appears to be buggy to build in windows with ghc 8.4 and 8.8
- ghc: '8.4'
cabal: '3.2'
os: windows-latest
experimental: true
- ghc: '8.8'
cabal: '3.2'
os: windows-latest
experimental: true
steps:
- uses: actions/checkout@v2
- uses: actions/setup-haskell@v1.1
with:
ghc-version: ${{ matrix.ghc }}
cabal-version: ${{ matrix.cabal }}
- name: Cache
uses: actions/cache@v2
env:
cache-name: cabal-cache-${{ matrix.ghc }}-${{ matrix.cabal }}
with:
path: |
~/.cabal
~/.stack
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/*.cabal') }}-${{ hashFiles('**/stack.yaml') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/*.cabal') }}-${{ hashFiles('**/stack.yaml') }}
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Before install (Linux)
if: matrix.os == 'ubuntu-latest'
run: |
mkdir -p /tmp/minio /tmp/minio-config/certs
cp test/cert/* /tmp/minio-config/certs/
(cd /tmp/minio; wget -q https://dl.min.io/server/minio/release/linux-amd64/minio; chmod +x ./minio)
sudo cp /tmp/minio-config/certs/public.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates
- name: Before install (MacOS)
if: matrix.os == 'macos-latest'
run: |
mkdir -p /tmp/minio /tmp/minio-config/certs
cp test/cert/* /tmp/minio-config/certs/
(cd /tmp/minio; wget -q https://dl.min.io/server/minio/release/darwin-amd64/minio; chmod +x ./minio)
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain /tmp/minio-config/certs/public.crt
- name: Before install (Windows)
if: matrix.os == 'windows-latest'
run: |
New-Item -ItemType Directory -Path "$env:temp/minio-config/certs/"
Copy-Item -Path test\cert\* -Destination "$env:temp/minio-config/certs/"
Invoke-WebRequest -Uri https://dl.minio.io/server/minio/release/windows-amd64/minio.exe -OutFile $HOME/minio.exe
Import-Certificate -FilePath "$env:temp/minio-config/certs/public.crt" -CertStoreLocation Cert:\LocalMachine\Root
- name: Install dependencies, build and test (Non-Windows)
if: matrix.os != 'windows-latest'
env:
MINIO_ACCESS_KEY: minio
MINIO_SECRET_KEY: minio123
MINIO_LOCAL: 1
MINIO_SECURE: 1
run: |
/tmp/minio/minio server --quiet --certs-dir /tmp/minio-config/certs data1 data2 data3 data4 2>&1 > minio.log &
ghc --version
cabal --version
cabal new-update
cabal new-build --enable-tests --enable-benchmarks -fexamples
cabal new-test --enable-tests -flive-test
- name: Install dependencies, build and test (Windows)
if: matrix.os == 'windows-latest'
env:
MINIO_ACCESS_KEY: minio
MINIO_SECRET_KEY: minio123
MINIO_LOCAL: 1
MINIO_SECURE: 1
run: |
Start-Process -NoNewWindow -FilePath "$HOME/minio.exe" -ArgumentList "--certs-dir", "$env:temp/minio-config/certs", "server", "$env:temp/data1", "$env:temp/data2", "$env:temp/data3", "$env:temp/data4"
ghc --version
cabal --version
cabal new-update
cabal new-build --enable-tests --enable-benchmarks -fexamples
cabal new-test --enable-tests -flive-test

105
.github/workflows/haskell-stack.yml vendored Normal file
View File

@ -0,0 +1,105 @@
name: Haskell CI (Stack)
on:
schedule:
# Run every weekday
- cron: '0 0 * * 1-5'
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
stack-build:
runs-on: ${{ matrix.os }}
continue-on-error: ${{ matrix.experimental }}
strategy:
fail-fast: false
matrix:
ghc: ['8.8']
cabal: ['3.2']
os: [ubuntu-latest, macOS-latest]
experimental: [false]
include:
# Appears to be buggy to build in windows with ghc 8.8
- ghc: '8.8'
cabal: '3.2'
os: windows-latest
experimental: true
steps:
- uses: actions/checkout@v2
- uses: actions/setup-haskell@v1.1
with:
ghc-version: ${{ matrix.ghc }}
cabal-version: ${{ matrix.cabal }}
enable-stack: true
- name: Cache
uses: actions/cache@v2
env:
cache-name: stack-cache-${{ matrix.ghc }}-${{ matrix.cabal }}
with:
path: |
~/.cabal
~/.stack
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/*.cabal') }}-${{ hashFiles('**/stack.yaml') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/*.cabal') }}-${{ hashFiles('**/stack.yaml') }}
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Before install (Linux)
if: matrix.os == 'ubuntu-latest'
run: |
mkdir -p /tmp/minio /tmp/minio-config/certs
cp test/cert/* /tmp/minio-config/certs/
(cd /tmp/minio; wget -q https://dl.min.io/server/minio/release/linux-amd64/minio; chmod +x ./minio)
sudo cp /tmp/minio-config/certs/public.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates
- name: Before install (MacOS)
if: matrix.os == 'macos-latest'
run: |
mkdir -p /tmp/minio /tmp/minio-config/certs
cp test/cert/* /tmp/minio-config/certs/
(cd /tmp/minio; wget -q https://dl.min.io/server/minio/release/darwin-amd64/minio; chmod +x ./minio)
sudo security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain /tmp/minio-config/certs/public.crt
- name: Before install (Windows)
if: matrix.os == 'windows-latest'
run: |
New-Item -ItemType Directory -Path "$env:temp/minio-config/certs/"
Copy-Item -Path test\cert\* -Destination "$env:temp/minio-config/certs/"
Invoke-WebRequest -Uri https://dl.minio.io/server/minio/release/windows-amd64/minio.exe -OutFile $HOME/minio.exe
Import-Certificate -FilePath "$env:temp/minio-config/certs/public.crt" -CertStoreLocation Cert:\LocalMachine\Root
- name: Install dependencies, build and test (Non-Windows)
if: matrix.os != 'windows-latest'
env:
MINIO_ACCESS_KEY: minio
MINIO_SECRET_KEY: minio123
MINIO_LOCAL: 1
MINIO_SECURE: 1
run: |
/tmp/minio/minio server --quiet --certs-dir /tmp/minio-config/certs data1 data2 data3 data4 2>&1 > minio.log &
ghc --version
stack --version
stack build --system-ghc --test --bench --no-run-tests --no-run-benchmarks --flag minio-hs:examples
stack test --system-ghc --flag minio-hs:live-test
- name: Install dependencies, build and test (Windows)
if: matrix.os == 'windows-latest'
env:
MINIO_ACCESS_KEY: minio
MINIO_SECRET_KEY: minio123
MINIO_LOCAL: 1
MINIO_SECURE: 1
run: |
Start-Process -NoNewWindow -FilePath "$HOME/minio.exe" -ArgumentList "--certs-dir", "$env:temp/minio-config/certs", "server", "$env:temp/data1", "$env:temp/data2", "$env:temp/data3", "$env:temp/data4"
ghc --version
stack --version
stack build --system-ghc --test --bench --no-run-tests --no-run-benchmarks --flag minio-hs:examples
stack test --system-ghc --flag minio-hs:live-test

View File

@ -1,54 +0,0 @@
name: Haskell CI
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
ghc: ['8.6.5', '8.8.3']
cabal: ['2.4', '3.0', '3.2']
os: [ubuntu-latest, macOS-latest, windows-latest]
exclude:
# GHC 8.8+ only works with cabal v3+
- ghc: 8.8.3
cabal: 2.4
# Appears to be buggy to build in windows
- ghc: 8.8.3
os: windows-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-haskell@v1.1
with:
ghc-version: ${{ matrix.ghc }}
cabal-version: ${{ matrix.cabal }}
- name: Cache
uses: actions/cache@v1
env:
cache-name: cache-cabal
with:
path: ~/.cabal
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/*.cabal') }}-${{ hashFiles('**/cabal.project') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- name: Install dependencies
run: |
cabal v2-update
cabal v2-build --only-dependencies --enable-tests --enable-benchmarks
- name: Build
run: cabal v2-build --enable-tests --enable-benchmarks all
- name: Run tests
run: cabal v2-test all

View File

@ -39,11 +39,11 @@ import Network.Minio.S3API
import Network.Minio.Utils
import System.Directory (getTemporaryDirectory)
import System.Environment (lookupEnv)
import qualified System.IO as SIO
import qualified Test.QuickCheck as Q
import Test.Tasty
import Test.Tasty.HUnit
import Test.Tasty.QuickCheck as QC
import qualified UnliftIO.IO as UIO
main :: IO ()
main = defaultMain tests
@ -72,7 +72,7 @@ randomDataSrc s' = genBS s'
mkRandFile :: R.MonadResource m => Int64 -> m FilePath
mkRandFile size = do
dir <- liftIO $ getTemporaryDirectory
dir <- liftIO getTemporaryDirectory
C.runConduit $ randomDataSrc size C..| CB.sinkTempFile dir "miniohstest.random"
funTestBucketPrefix :: Text
@ -158,17 +158,18 @@ basicTests = funTestWithBucket "Basic tests" $
liftIO $ region == "us-east-1" @? ("Got unexpected region => " ++ show region)
step "singlepart putObject works"
fPutObject bucket "lsb-release" "/etc/lsb-release" defaultPutObjectOptions
testFilepath <- mkRandFile 200
fPutObject bucket "test-file" testFilepath defaultPutObjectOptions
step "fPutObject onto a non-existent bucket and check for NoSuchBucket exception"
fpE <- try $ fPutObject "nosuchbucket" "lsb-release" "/etc/lsb-release" defaultPutObjectOptions
fpE <- try $ fPutObject "nosuchbucket" "test-file-2" testFilepath defaultPutObjectOptions
case fpE of
Left exn -> liftIO $ exn @?= NoSuchBucket
_ -> return ()
outFile <- mkRandFile 0
step "simple fGetObject works"
fGetObject bucket "lsb-release" outFile defaultGetObjectOptions
fGetObject bucket "test-file" outFile defaultGetObjectOptions
let unmodifiedTime = UTCTime (fromGregorian 2010 11 26) 69857
step "fGetObject an object which is modified now but requesting as un-modified in past, check for exception"
@ -176,7 +177,7 @@ basicTests = funTestWithBucket "Basic tests" $
try $
fGetObject
bucket
"lsb-release"
"test-file"
outFile
defaultGetObjectOptions
{ gooIfUnmodifiedSince = (Just unmodifiedTime)
@ -190,7 +191,7 @@ basicTests = funTestWithBucket "Basic tests" $
try $
fGetObject
bucket
"lsb-release"
"test-file"
outFile
defaultGetObjectOptions
{ gooIfMatch = (Just "invalid-etag")
@ -204,10 +205,10 @@ basicTests = funTestWithBucket "Basic tests" $
try $
fGetObject
bucket
"lsb-release"
"test-file"
outFile
defaultGetObjectOptions
{ gooRange = (Just $ HT.ByteRangeFromTo 100 200)
{ gooRange = (Just $ HT.ByteRangeFromTo 100 300)
}
case resE2 of
Left exn -> liftIO $ exn @?= ServiceErr "InvalidRange" "The requested range is not satisfiable"
@ -216,7 +217,7 @@ basicTests = funTestWithBucket "Basic tests" $
step "fGetObject on object with a valid range"
fGetObject
bucket
"lsb-release"
"test-file"
outFile
defaultGetObjectOptions
{ gooRange = (Just $ HT.ByteRangeFrom 1)
@ -236,7 +237,7 @@ basicTests = funTestWithBucket "Basic tests" $
abortMultipartUpload bucket "newmpupload" uid
step "delete object works"
deleteObject bucket "lsb-release"
deleteObject bucket "test-file"
step "statObject test"
let object = "sample"
@ -266,8 +267,9 @@ lowLevelMultipartTest = funTestWithBucket "Low-level Multipart Test" $
randFile <- mkRandFile mb15
step "put object parts 1 of 1"
h <- liftIO $ SIO.openBinaryFile randFile SIO.ReadMode
partInfo <- putObjectPart bucket object uid 1 [] $ PayloadH h 0 mb15
partInfo <-
UIO.withBinaryFile randFile UIO.ReadMode $ \h ->
putObjectPart bucket object uid 1 [] $ PayloadH h 0 mb15
step "complete multipart"
void $ completeMultipartUpload bucket object uid [partInfo]
@ -353,8 +355,9 @@ highLevelListingTest = funTestWithBucket "High-level listObjects Test" $
)
os
testFilepath <- mkRandFile 200
forM_ expectedObjects $
\obj -> fPutObject bucket obj "/etc/lsb-release" defaultPutObjectOptions
\obj -> fPutObject bucket obj testFilepath defaultPutObjectOptions
step "High-level listing of objects"
items <- C.runConduit $ listObjects bucket Nothing False C..| sinkList
@ -452,9 +455,9 @@ highLevelListingTest = funTestWithBucket "High-level listObjects Test" $
step "put object parts 1..10"
inputFile <- mkRandFile mb5
h <- liftIO $ SIO.openBinaryFile inputFile SIO.ReadMode
forM_ [1 .. 10] $ \pnum ->
putObjectPart bucket object uid pnum [] $ PayloadH h 0 mb5
UIO.withBinaryFile inputFile UIO.ReadMode $ \h ->
forM_ [1 .. 10] $ \pnum ->
putObjectPart bucket object uid pnum [] $ PayloadH h 0 mb5
step "fetch list parts"
incompleteParts <-
@ -470,10 +473,11 @@ listingTest :: TestTree
listingTest = funTestWithBucket "Listing Test" $ \step bucket -> do
step "listObjects' test"
step "put 10 objects"
let objects = (\s -> T.concat ["lsb-release", T.pack (show s)]) <$> [1 .. 10 :: Int]
let objects = (\s -> T.concat ["test-file-", T.pack (show s)]) <$> [1 .. 10 :: Int]
testFilepath <- mkRandFile 200
forM_ [1 .. 10 :: Int] $ \s ->
fPutObject bucket (T.concat ["lsb-release", T.pack (show s)]) "/etc/lsb-release" defaultPutObjectOptions
fPutObject bucket (T.concat ["test-file-", T.pack (show s)]) testFilepath defaultPutObjectOptions
step "Simple list"
res <- listObjects' bucket Nothing Nothing Nothing Nothing
@ -490,7 +494,7 @@ listingTest = funTestWithBucket "Listing Test" $ \step bucket -> do
sort $
map
( T.concat
. ("lsb-release" :)
. ("test-file-" :)
. (\x -> [x])
. T.pack
. show
@ -536,9 +540,9 @@ listingTest = funTestWithBucket "Listing Test" $ \step bucket -> do
step "put object parts 1..10"
inputFile <- mkRandFile mb5
h <- liftIO $ SIO.openBinaryFile inputFile SIO.ReadMode
forM_ [1 .. 10] $ \pnum ->
putObjectPart bucket object uid pnum [] $ PayloadH h 0 mb5
UIO.withBinaryFile inputFile UIO.ReadMode $ \h ->
forM_ [1 .. 10] $ \pnum ->
putObjectPart bucket object uid pnum [] $ PayloadH h 0 mb5
step "fetch list parts"
listPartsResult <- listIncompleteParts' bucket object uid Nothing Nothing
@ -754,26 +758,28 @@ bucketPolicyFunTest = funTestWithBucket "Bucket Policy tests" $
multipartTest :: TestTree
multipartTest = funTestWithBucket "Multipart Tests" $
\step bucket -> do
step "Prepare for putObjectInternal with non-seekable file, with size."
step "Upload multipart file."
let mb80 = 80 * 1024 * 1024
obj = "mpart"
-- Commenting out test since it's platform specific.
-- FIXME: Need to find a platform agnostic way to test this.
-- step "Prepare for putObjectInternal with non-seekable file, with size."
-- step "Upload multipart file."
-- let mb80 = 80 * 1024 * 1024
-- obj = "mpart"
void $ putObjectInternal bucket obj defaultPutObjectOptions $ ODFile "/dev/zero" (Just mb80)
-- void $ putObjectInternal bucket obj defaultPutObjectOptions $ ODFile "/dev/zero" (Just mb80)
step "Retrieve and verify file size"
destFile <- mkRandFile 0
fGetObject bucket obj destFile defaultGetObjectOptions
gotSize <- withNewHandle destFile getFileSize
liftIO $
gotSize == Right (Just mb80)
@? "Wrong file size of put file after getting"
-- step "Retrieve and verify file size"
-- destFile <- mkRandFile 0
-- fGetObject bucket obj destFile defaultGetObjectOptions
-- gotSize <- withNewHandle destFile getFileSize
-- liftIO $
-- gotSize == Right (Just mb80)
-- @? "Wrong file size of put file after getting"
step "Cleanup actions"
removeObject bucket obj
-- step "Cleanup actions"
-- removeObject bucket obj
step "cleanup"
removeObject bucket "big"
-- step "cleanup"
-- removeObject bucket "big"
step "Prepare for removeIncompleteUpload"
-- low-level multipart operation tests.
@ -788,8 +794,8 @@ multipartTest = funTestWithBucket "Multipart Tests" $
step "upload 2 parts"
forM_ [1, 2] $ \partNum -> do
h <- liftIO $ SIO.openBinaryFile randFile SIO.ReadMode
void $ putObjectPart bucket object uid partNum [] $ PayloadH h 0 kb5
UIO.withBinaryFile randFile UIO.ReadMode $ \h ->
void $ putObjectPart bucket object uid partNum [] $ PayloadH h 0 kb5
step "remove ongoing upload"
removeIncompleteUpload bucket object

28
test/cert/private.key Normal file
View File

@ -0,0 +1,28 @@
-----BEGIN PRIVATE KEY-----
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC3G9IiC+adjf0p
i/2KYc+4dizeuzUFN7wraSdhiOMdQgCnu9Dc3t2YEsQhNdrARjOTyXd36KeM3TwI
rPJ61dRGQSuN12l+mzngFJQjE0sysZHUJOLQC3rVvIrHSQ57utPg8ifxt/SunlPY
fhcUcq03onMGq44yOfE6mIhoe0Y9wcPQ3RjjNNS44bgmXiXwa+Do0h2hEn6/essq
5KjHL8WW2vGg7G9edpYdxINA/A2fdLtr8BwPNrZhOx84eee2XcUNdBuTtUUxE+0L
9yRqItqddriRxJFwOXb5OPW8xx2WGaV2a0wbE4gB2PTwwDvfo72mo9HXHZUHM1A8
4TD/RXMbAgMBAAECggEBAJ7r1oUWLyGvinn0tijUm6RNbMQjVvEgXoCO008jr3pF
PqxVpgEMrOa/4tmwFBus0jcCNF4t3r2zhddBw3I5A/O1vEdvHnBz6NdDBQ8sP6fP
1fF50iEe1Y2MBibQkXFxxVMG2QRB1Gt5nuvXA9ELdqtCovK3EsMk5ukkWb/UvjH5
8hcmQsaSqvzFEF4wJSY2mkeGSGIJTphPhhuA22xbhaBMInQyhZu8EHsn0h6s/Wgy
C4Cp2+4qZTKaaf6x3/ZjJ8CuKiSX+ZsJKjOEv8sqx7j/Y7QFOmJPewInKDhwazr/
xIK+N0KXPbUzeSEz6ZvExNDTxtR5ZlQP2UrRDg28yQECgYEA4Is1O2BvKVzNFOkj
bTVz25a/bb0Xrcfgi0Y9rdfLzlNdItFjAkxLTVRSW2Hv9ICl0RDDAG+wTlktXRdh
rfvDjwG2CvLQo1VEdMWTTkKVg03SwMEy2hFiWV69lENFGSaY8Y6unZDbia5HQinA
EgSS4sCojS+a2jtzG5FVVHJDKlkCgYEA0MKhMhD4SUhr2y1idPBrmLxuW5mVozuW
8bYaBeSzmfS0BRsN4fP9JGODPBPDdNbfGfGC9ezWLgD/lmCgjIEyBOq8EmqWSsiS
Kihds1+Z7hXtbzGsFGAFJJTIh7blBCsK5QFuyuih2UG0fL9z6K/dy+UUJkzrYqph
vSfKixyM8pMCgYEAmUPLsNyw4325aeV8TeWnUCJERaZFDFQa21W1cfyS2yEhuEtN
llr3JzBACqn9vFk3VU1onNqfb8sE4L696KCpKeqUFEMK0AG6eS4Gzus53Gb5TKJS
kHA/PhshsZp9Bp7G1FJ8s4YVo5N2hh2zQVkn3Wh9Y+kzfHQJrK51nO9lEvkCgYBi
BuKWle1gzAcJdnhDHRoJMIJJtQbVDYhFnBMALXJAmu1lcFzGe0GlMq1PKqCfXr6I
eiXawQmZtJJP1LPPBmOsd2U06KQGHcS00xucvQmVCOrjSdnZ/3SqxsqbH8DOgj+t
ZUzXLwHA+N99rJEK9Hob4kfh7ECjpgobPnIXfKKazQKBgQChAuiXHtf/Qq18hY3u
x48zFWjGgfd6GpOBZYkXOwGdCJgnYjZbE26LZEnYbwPh8ZUA2vp7mgHRJkD5e3Fj
ERuJLCw86WqyYZmLEuBciYGjCZqR5nbavfwsziWD00jeNruds2ZwKxRfFm4V7o2S
WLd/RUatd2Uu9f3B2J78OUdnxg==
-----END PRIVATE KEY-----

19
test/cert/public.crt Normal file
View File

@ -0,0 +1,19 @@
-----BEGIN CERTIFICATE-----
MIIDCzCCAfOgAwIBAgIUaIUOMI78LCu+r1zl0mmFHK8n5/AwDQYJKoZIhvcNAQEL
BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MCAXDTE5MTAyNDE5NTMxOVoYDzIxMTkw
OTMwMTk1MzE5WjAUMRIwEAYDVQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQC3G9IiC+adjf0pi/2KYc+4dizeuzUFN7wraSdhiOMd
QgCnu9Dc3t2YEsQhNdrARjOTyXd36KeM3TwIrPJ61dRGQSuN12l+mzngFJQjE0sy
sZHUJOLQC3rVvIrHSQ57utPg8ifxt/SunlPYfhcUcq03onMGq44yOfE6mIhoe0Y9
wcPQ3RjjNNS44bgmXiXwa+Do0h2hEn6/essq5KjHL8WW2vGg7G9edpYdxINA/A2f
dLtr8BwPNrZhOx84eee2XcUNdBuTtUUxE+0L9yRqItqddriRxJFwOXb5OPW8xx2W
GaV2a0wbE4gB2PTwwDvfo72mo9HXHZUHM1A84TD/RXMbAgMBAAGjUzBRMB0GA1Ud
DgQWBBSEWXQ2JRD+OK7/KTmlD+OW16pGmzAfBgNVHSMEGDAWgBSEWXQ2JRD+OK7/
KTmlD+OW16pGmzAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCF
0zYRaabB3X0jzGI9/Lr3Phrb90GvoL1DFLRuiOuTlDkz0vrm/HrZskwHCgMNrkCj
OTD9Vpas4D1QZBbQbRzfnf3OOoG4bgmcCwLFZl3dy27yIDAhrmbUP++g9l1Jmy4v
vBR/M4lt2scQ8LcZYEPqhEaE5EzFQEjtaxDcKdWDNKY9W1NUzSIABhF9eHiAUNdH
AFNJlYeBlCHxcWIeqgon184Dqp/CsvKtz3z3Ni+rlwPM/zuJCFHh1VF+z++0LJjG
roBCV0Tro4XyiEz9yp7Cb5kQYMaj1KL9TqBG0tZx0pmv7y+lXc4TT6DEllXz6USy
rbIba9/uUet3BqeIMTqj
-----END CERTIFICATE-----