From 1c9a188600346cac00c7fc6b1740a36938a26207 Mon Sep 17 00:00:00 2001 From: Vitaliy Filippov Date: Sun, 30 Apr 2023 01:01:52 +0300 Subject: [PATCH] Add tests to CI --- .gitea/workflows/buildenv.Dockerfile | 36 ++ .gitea/workflows/test.Dockerfile | 16 + .gitea/workflows/test.yml | 552 +++++++++++++++++++++++++++ .gitea/workflows/tests-to-yaml.pl | 68 ++++ docker/vitastor.gpg | Bin 1769 -> 1759 bytes 5 files changed, 672 insertions(+) create mode 100644 .gitea/workflows/buildenv.Dockerfile create mode 100644 .gitea/workflows/test.Dockerfile create mode 100644 .gitea/workflows/test.yml create mode 100755 .gitea/workflows/tests-to-yaml.pl diff --git a/.gitea/workflows/buildenv.Dockerfile b/.gitea/workflows/buildenv.Dockerfile new file mode 100644 index 00000000..4e4ae6d1 --- /dev/null +++ b/.gitea/workflows/buildenv.Dockerfile @@ -0,0 +1,36 @@ +FROM node:16-bullseye + +WORKDIR /root + +ADD ./docker/vitastor.gpg /etc/apt/trusted.gpg.d + +RUN echo 'deb http://deb.debian.org/debian bullseye-backports main' >> /etc/apt/sources.list; \ + echo 'deb http://vitastor.io/debian bullseye main' >> /etc/apt/sources.list; \ + echo >> /etc/apt/preferences; \ + echo 'Package: *' >> /etc/apt/preferences; \ + echo 'Pin: release a=bullseye-backports' >> /etc/apt/preferences; \ + echo 'Pin-Priority: 500' >> /etc/apt/preferences; \ + echo >> /etc/apt/preferences; \ + echo 'Package: *' >> /etc/apt/preferences; \ + echo 'Pin: origin "vitastor.io"' >> /etc/apt/preferences; \ + echo 'Pin-Priority: 1000' >> /etc/apt/preferences; \ + grep '^deb ' /etc/apt/sources.list | perl -pe 's/^deb/deb-src/' >> /etc/apt/sources.list; \ + echo 'APT::Install-Recommends false;' >> /etc/apt/apt.conf; \ + echo 'APT::Install-Suggests false;' >> /etc/apt/apt.conf + +RUN apt-get update +RUN apt-get -y install etcd qemu-system-x86 qemu-block-extra qemu-utils fio libasan5 \ + liburing1 liburing-dev libgoogle-perftools-dev devscripts libjerasure-dev cmake libibverbs-dev libisal-dev +RUN apt-get -y build-dep fio qemu=`dpkg -s qemu-system-x86|grep ^Version:|awk '{print $2}'` +RUN apt-get -y install jq lp-solve sudo +RUN apt-get --download-only source fio qemu=`dpkg -s qemu-system-x86|grep ^Version:|awk '{print $2}'` + +RUN set -ex; \ + mkdir qemu-build; \ + cd qemu-build; \ + dpkg-source -x /root/qemu*.dsc; \ + cd qemu*/; \ + debian/rules configure-qemu || debian/rules b/configure-stamp; \ + cd b/qemu; \ + make -j8 config-poison.h || true; \ + make -j8 qapi/qapi-builtin-types.h diff --git a/.gitea/workflows/test.Dockerfile b/.gitea/workflows/test.Dockerfile new file mode 100644 index 00000000..736daafe --- /dev/null +++ b/.gitea/workflows/test.Dockerfile @@ -0,0 +1,16 @@ +FROM git.yourcmc.ru/vitalif/vitastor/buildenv + +ADD . /root/vitastor + +RUN set -e -x; \ + mkdir -p /root/fio-build/; \ + cd /root/fio-build/; \ + dpkg-source -x /root/fio*.dsc; \ + cd /root/vitastor; \ + ln -s /root/fio-build/fio-*/ ./fio; \ + ln -s /root/qemu-build/qemu-*/ ./qemu; \ + ls /usr/include/linux/raw.h || cp ./debian/raw.h /usr/include/linux/raw.h; \ + mkdir build; \ + cd build; \ + cmake .. -DWITH_ASAN=yes -DWITH_QEMU=yes; \ + make -j16 diff --git a/.gitea/workflows/test.yml b/.gitea/workflows/test.yml new file mode 100644 index 00000000..6c8c01df --- /dev/null +++ b/.gitea/workflows/test.yml @@ -0,0 +1,552 @@ +name: Test + +on: + push: + branches: + - '*' + paths: + - '.gitea/**' + - 'src/**' + - 'mon/**' + - 'json11' + - 'cpp-btree' + - 'tests/**' + +env: + BUILDENV_IMAGE: git.yourcmc.ru/vitalif/vitastor/buildenv + TEST_IMAGE: git.yourcmc.ru/vitalif/vitastor/test + OSD_ARGS: '--etcd_quick_timeout 2000' + +concurrency: + group: ci-${{ github.ref }} + cancel-in-progress: true + +jobs: + + buildenv: + runs-on: ubuntu-latest + container: git.yourcmc.ru/vitalif/gitea-ci-dind + steps: + - uses: actions/checkout@v3 + + - name: Build and push + run: | + set -ex + if ! docker manifest inspect $BUILDENV_IMAGE >/dev/null; then + docker build -t $BUILDENV_IMAGE -f .gitea/workflows/buildenv.Dockerfile . + docker login git.yourcmc.ru -u vitalif -p "${{secrets.TOKEN}}" + docker push $BUILDENV_IMAGE + fi + + build: + runs-on: ubuntu-latest + needs: buildenv + container: git.yourcmc.ru/vitalif/gitea-ci-dind + steps: + - uses: actions/checkout@v3 + with: + submodules: true + + - name: Build and push + run: | + set -ex + if ! docker manifest inspect $TEST_IMAGE:$GITHUB_SHA >/dev/null; then + docker build -t $TEST_IMAGE:$GITHUB_SHA -f .gitea/workflows/test.Dockerfile . + docker login git.yourcmc.ru -u vitalif -p "${{secrets.TOKEN}}" + docker push $TEST_IMAGE:$GITHUB_SHA + fi + + make_test: + runs-on: ubuntu-latest + needs: build + container: ${{env.TEST_IMAGE}}:${{github.sha}} + steps: + # leak sanitizer sometimes crashes + - run: cd /root/vitastor/build && ASAN_OPTIONS=detect_leaks=0 make -j16 test + + test_add_osd: + runs-on: ubuntu-latest + needs: build + container: ${{env.TEST_IMAGE}}:${{github.sha}} + steps: + - name: Run test + id: test + timeout-minutes: 3 + run: /root/vitastor/tests/test_add_osd.sh + - name: Print logs + if: always() && steps.test.outcome == 'failure' + run: | + for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do + echo "-------- $i --------" + cat $i + echo "" + done + + test_cas: + runs-on: ubuntu-latest + needs: build + container: ${{env.TEST_IMAGE}}:${{github.sha}} + steps: + - name: Run test + id: test + timeout-minutes: 3 + run: /root/vitastor/tests/test_cas.sh + - name: Print logs + if: always() && steps.test.outcome == 'failure' + run: | + for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do + echo "-------- $i --------" + cat $i + echo "" + done + + test_change_pg_count: + runs-on: ubuntu-latest + needs: build + container: ${{env.TEST_IMAGE}}:${{github.sha}} + steps: + - name: Run test + id: test + timeout-minutes: 3 + run: /root/vitastor/tests/test_change_pg_count.sh + - name: Print logs + if: always() && steps.test.outcome == 'failure' + run: | + for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do + echo "-------- $i --------" + cat $i + echo "" + done + + test_change_pg_count_ec: + runs-on: ubuntu-latest + needs: build + container: ${{env.TEST_IMAGE}}:${{github.sha}} + steps: + - name: Run test + id: test + timeout-minutes: 3 + run: SCHEME=ec /root/vitastor/tests/test_change_pg_count.sh + - name: Print logs + if: always() && steps.test.outcome == 'failure' + run: | + for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do + echo "-------- $i --------" + cat $i + echo "" + done + + test_change_pg_size: + runs-on: ubuntu-latest + needs: build + container: ${{env.TEST_IMAGE}}:${{github.sha}} + steps: + - name: Run test + id: test + timeout-minutes: 3 + run: /root/vitastor/tests/test_change_pg_size.sh + - name: Print logs + if: always() && steps.test.outcome == 'failure' + run: | + for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do + echo "-------- $i --------" + cat $i + echo "" + done + + test_create_nomaxid: + runs-on: ubuntu-latest + needs: build + container: ${{env.TEST_IMAGE}}:${{github.sha}} + steps: + - name: Run test + id: test + timeout-minutes: 3 + run: /root/vitastor/tests/test_create_nomaxid.sh + - name: Print logs + if: always() && steps.test.outcome == 'failure' + run: | + for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do + echo "-------- $i --------" + cat $i + echo "" + done + + test_etcd_fail: + runs-on: ubuntu-latest + needs: build + container: ${{env.TEST_IMAGE}}:${{github.sha}} + steps: + - name: Run test + id: test + timeout-minutes: 10 + run: /root/vitastor/tests/test_etcd_fail.sh + - name: Print logs + if: always() && steps.test.outcome == 'failure' + run: | + for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do + echo "-------- $i --------" + cat $i + echo "" + done + + test_failure_domain: + runs-on: ubuntu-latest + needs: build + container: ${{env.TEST_IMAGE}}:${{github.sha}} + steps: + - name: Run test + id: test + timeout-minutes: 3 + run: /root/vitastor/tests/test_failure_domain.sh + - name: Print logs + if: always() && steps.test.outcome == 'failure' + run: | + for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do + echo "-------- $i --------" + cat $i + echo "" + done + + test_interrupted_rebalance: + runs-on: ubuntu-latest + needs: build + container: ${{env.TEST_IMAGE}}:${{github.sha}} + steps: + - name: Run test + id: test + timeout-minutes: 10 + run: /root/vitastor/tests/test_interrupted_rebalance.sh + - name: Print logs + if: always() && steps.test.outcome == 'failure' + run: | + for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do + echo "-------- $i --------" + cat $i + echo "" + done + + test_interrupted_rebalance_imm: + runs-on: ubuntu-latest + needs: build + container: ${{env.TEST_IMAGE}}:${{github.sha}} + steps: + - name: Run test + id: test + timeout-minutes: 10 + run: IMMEDIATE_COMMIT=1 /root/vitastor/tests/test_interrupted_rebalance.sh + - name: Print logs + if: always() && steps.test.outcome == 'failure' + run: | + for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do + echo "-------- $i --------" + cat $i + echo "" + done + + test_interrupted_rebalance_ec: + runs-on: ubuntu-latest + needs: build + container: ${{env.TEST_IMAGE}}:${{github.sha}} + steps: + - name: Run test + id: test + timeout-minutes: 10 + run: SCHEME=ec /root/vitastor/tests/test_interrupted_rebalance.sh + - name: Print logs + if: always() && steps.test.outcome == 'failure' + run: | + for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do + echo "-------- $i --------" + cat $i + echo "" + done + + test_interrupted_rebalance_ec_imm: + runs-on: ubuntu-latest + needs: build + container: ${{env.TEST_IMAGE}}:${{github.sha}} + steps: + - name: Run test + id: test + timeout-minutes: 10 + run: SCHEME=ec IMMEDIATE_COMMIT=1 /root/vitastor/tests/test_interrupted_rebalance.sh + - name: Print logs + if: always() && steps.test.outcome == 'failure' + run: | + for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do + echo "-------- $i --------" + cat $i + echo "" + done + + test_minsize_1: + runs-on: ubuntu-latest + needs: build + container: ${{env.TEST_IMAGE}}:${{github.sha}} + steps: + - name: Run test + id: test + timeout-minutes: 3 + run: /root/vitastor/tests/test_minsize_1.sh + - name: Print logs + if: always() && steps.test.outcome == 'failure' + run: | + for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do + echo "-------- $i --------" + cat $i + echo "" + done + + test_move_reappear: + runs-on: ubuntu-latest + needs: build + container: ${{env.TEST_IMAGE}}:${{github.sha}} + steps: + - name: Run test + id: test + timeout-minutes: 3 + run: /root/vitastor/tests/test_move_reappear.sh + - name: Print logs + if: always() && steps.test.outcome == 'failure' + run: | + for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do + echo "-------- $i --------" + cat $i + echo "" + done + + test_rebalance_verify: + runs-on: ubuntu-latest + needs: build + container: ${{env.TEST_IMAGE}}:${{github.sha}} + steps: + - name: Run test + id: test + timeout-minutes: 3 + run: /root/vitastor/tests/test_rebalance_verify.sh + - name: Print logs + if: always() && steps.test.outcome == 'failure' + run: | + for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do + echo "-------- $i --------" + cat $i + echo "" + done + + test_rebalance_verify_imm: + runs-on: ubuntu-latest + needs: build + container: ${{env.TEST_IMAGE}}:${{github.sha}} + steps: + - name: Run test + id: test + timeout-minutes: 3 + run: IMMEDIATE_COMMIT=1 /root/vitastor/tests/test_rebalance_verify.sh + - name: Print logs + if: always() && steps.test.outcome == 'failure' + run: | + for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do + echo "-------- $i --------" + cat $i + echo "" + done + + test_rebalance_verify_ec: + runs-on: ubuntu-latest + needs: build + container: ${{env.TEST_IMAGE}}:${{github.sha}} + steps: + - name: Run test + id: test + timeout-minutes: 3 + run: SCHEME=ec /root/vitastor/tests/test_rebalance_verify.sh + - name: Print logs + if: always() && steps.test.outcome == 'failure' + run: | + for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do + echo "-------- $i --------" + cat $i + echo "" + done + + test_rebalance_verify_ec_imm: + runs-on: ubuntu-latest + needs: build + container: ${{env.TEST_IMAGE}}:${{github.sha}} + steps: + - name: Run test + id: test + timeout-minutes: 3 + run: SCHEME=ec IMMEDIATE_COMMIT=1 /root/vitastor/tests/test_rebalance_verify.sh + - name: Print logs + if: always() && steps.test.outcome == 'failure' + run: | + for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do + echo "-------- $i --------" + cat $i + echo "" + done + + test_rm: + runs-on: ubuntu-latest + needs: build + container: ${{env.TEST_IMAGE}}:${{github.sha}} + steps: + - name: Run test + id: test + timeout-minutes: 3 + run: /root/vitastor/tests/test_rm.sh + - name: Print logs + if: always() && steps.test.outcome == 'failure' + run: | + for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do + echo "-------- $i --------" + cat $i + echo "" + done + + test_snapshot: + runs-on: ubuntu-latest + needs: build + container: ${{env.TEST_IMAGE}}:${{github.sha}} + steps: + - name: Run test + id: test + timeout-minutes: 3 + run: /root/vitastor/tests/test_snapshot.sh + - name: Print logs + if: always() && steps.test.outcome == 'failure' + run: | + for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do + echo "-------- $i --------" + cat $i + echo "" + done + + test_snapshot_ec: + runs-on: ubuntu-latest + needs: build + container: ${{env.TEST_IMAGE}}:${{github.sha}} + steps: + - name: Run test + id: test + timeout-minutes: 3 + run: SCHEME=ec /root/vitastor/tests/test_snapshot.sh + - name: Print logs + if: always() && steps.test.outcome == 'failure' + run: | + for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do + echo "-------- $i --------" + cat $i + echo "" + done + + test_splitbrain: + runs-on: ubuntu-latest + needs: build + container: ${{env.TEST_IMAGE}}:${{github.sha}} + steps: + - name: Run test + id: test + timeout-minutes: 3 + run: /root/vitastor/tests/test_splitbrain.sh + - name: Print logs + if: always() && steps.test.outcome == 'failure' + run: | + for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do + echo "-------- $i --------" + cat $i + echo "" + done + + test_write: + runs-on: ubuntu-latest + needs: build + container: ${{env.TEST_IMAGE}}:${{github.sha}} + steps: + - name: Run test + id: test + timeout-minutes: 3 + run: /root/vitastor/tests/test_write.sh + - name: Print logs + if: always() && steps.test.outcome == 'failure' + run: | + for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do + echo "-------- $i --------" + cat $i + echo "" + done + + test_write_xor: + runs-on: ubuntu-latest + needs: build + container: ${{env.TEST_IMAGE}}:${{github.sha}} + steps: + - name: Run test + id: test + timeout-minutes: 3 + run: SCHEME=xor /root/vitastor/tests/test_write.sh + - name: Print logs + if: always() && steps.test.outcome == 'failure' + run: | + for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do + echo "-------- $i --------" + cat $i + echo "" + done + + test_write_no_same: + runs-on: ubuntu-latest + needs: build + container: ${{env.TEST_IMAGE}}:${{github.sha}} + steps: + - name: Run test + id: test + timeout-minutes: 3 + run: /root/vitastor/tests/test_write_no_same.sh + - name: Print logs + if: always() && steps.test.outcome == 'failure' + run: | + for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do + echo "-------- $i --------" + cat $i + echo "" + done + + test_heal_pg_size_2: + runs-on: ubuntu-latest + needs: build + container: ${{env.TEST_IMAGE}}:${{github.sha}} + steps: + - name: Run test + id: test + timeout-minutes: 10 + run: PG_SIZE=2 /root/vitastor/tests/test_heal.sh + - name: Print logs + if: always() && steps.test.outcome == 'failure' + run: | + for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do + echo "-------- $i --------" + cat $i + echo "" + done + + test_heal_ec: + runs-on: ubuntu-latest + needs: build + container: ${{env.TEST_IMAGE}}:${{github.sha}} + steps: + - name: Run test + id: test + timeout-minutes: 10 + run: SCHEME=ec /root/vitastor/tests/test_heal.sh + - name: Print logs + if: always() && steps.test.outcome == 'failure' + run: | + for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do + echo "-------- $i --------" + cat $i + echo "" + done + diff --git a/.gitea/workflows/tests-to-yaml.pl b/.gitea/workflows/tests-to-yaml.pl new file mode 100755 index 00000000..5a2b9a1c --- /dev/null +++ b/.gitea/workflows/tests-to-yaml.pl @@ -0,0 +1,68 @@ +#!/usr/bin/perl + +use strict; + +for my $line (<>) +{ + if ($line =~ /\.\/(test_[^\.]+)/s) + { + chomp $line; + my $test_name = $1; + my $timeout = 3; + if ($test_name eq 'test_etcd_fail' || $test_name eq 'test_heal' || $test_name eq 'test_interrupted_rebalance') + { + $timeout = 10; + } + while ($line =~ /([^\s=]+)=(\S+)/gs) + { + if ($1 eq 'SCHEME' && $2 eq 'ec') + { + $test_name .= '_ec'; + } + elsif ($1 eq 'SCHEME' && $2 eq 'xor') + { + $test_name .= '_xor'; + } + elsif ($1 eq 'IMMEDIATE_COMMIT') + { + $test_name .= '_imm'; + } + else + { + $test_name .= '_'.lc($1).'_'.$2; + } + } + $line =~ s!\./test_!/root/vitastor/tests/test_!; + # Gitea CI doesn't support artifacts yet, lol + #- name: Upload results + # uses: actions/upload-artifact\@v3 + # if: always() + # with: + # name: ${test_name}_result + # path: | + # /root/vitastor/testdata + # !/root/vitastor/testdata/*.bin + # retention-days: 5 + print <<"EOF" + $test_name: + runs-on: ubuntu-latest + needs: build + container: \${{env.TEST_IMAGE}}:\${{github.sha}} + steps: + - name: Run test + id: test + timeout-minutes: $timeout + run: $line + - name: Print logs + if: always() && steps.test.outcome == 'failure' + run: | + for i in /root/vitastor/testdata/*.log /root/vitastor/testdata/*.txt; do + echo "-------- \$i --------" + cat \$i + echo "" + done + +EOF +; + } +} diff --git a/docker/vitastor.gpg b/docker/vitastor.gpg index f5150ed133dfe04b6fead82be937ce90dc4c7e06..1ff33ecd0805822c1bff80694ec34b119b5ad10e 100644 GIT binary patch literal 1759 zcmV<51|a#F0gVJ>a%NEh3;@!h`G|Q}k~GbAJKM{OtHo$tkv7ezc-dGwb_jyU2IXe~69UXHvrFbH~ z*rj`3l`*As{hSZ;m_Tmos|vNTJKP5_g31tnsB!%JtRf2{tfA>s_Vx;<)I>gX(%Z{F zCsH1lu#IbxcbrY}kY0ab>_#MzrC(Ue zAJDaN%vw!pHs-Q;(XjUagR#d#I9pw5R7g6JCis>+B7VNAfuATKJ_OB6B!D%jXH$DdHH9hZm{(_zDl;6WJ3*m$t4y)!2Ugx%%?# zS}P^dc4l!poCzDM{iKZX3g}jg4ly%;3FmU(4AyA6-5{G;zM}vU0RRECGgfJIVRLkE zav)G)V{2h&Wgt^&XKrb3XCO;uc_1<%Ja%bxVQgt;KzVO7~i2=?869EbU zI2IuUu}_6Z{H&#v#(0(@Vbpw<>7#Tn1p;GoW>Eqg0|g5S2nPZN6$%Lm3jzcd0s{d8 z9svRufB*^!5Mk7Omg%E(FAS9n|6~XePogU7$hkG-WC9w)ydmr$o3?6;ccqz z+QYSL#q$vKUp4r6q#Mqn=MoGkcPt4KG@~FgV{N%Y0dW0T8tNGnS@0hW*O?_qHbdV0_$+_MUIo1?fuO~%OCwUUE0jT%y2^0+j=RMuS)fon((X-#p960&H|cMsks4-1Y>e$Q2`79 z#s$IIr(H6UML<8Y4rPTlXJIb!Hm65I8Q1mwIQ3AV6;7LcM(n#|{6~UlZYAWHsyZn` z;IcoaOXK(@HElGAoTFx0NEjB~Wvr+F0UAo7USVk`k%7`?7vWMQpb|91j86gT1yS!9 zAa;ea=*uoYB-r=AI({E+K>AE*zWxC1K><|ABt_isH*%jZV<6p23>M%=Vaavy@-c`0`n7}G*Y9o-pcFZVmw z3Z!!@cui?qbWGyMwSfH>Lu=u*zo^%hh606*D;H29^VtGfjPTdd$V2CIo*f67$aWxFojKvze3M#-2CPJA%~1G)}OB zQRZLbUS!7`6i(~)Nyn2@qk_e3t2>bT3cu3;#^>osR2TK(L)#%mtMU<(P54byJ8* zAcsy8H^3K(|0w^8EdjHfAok4CAOrWqt;kV~5+^Q$Xh0KJL_#>|HR4Yr#o?Hqh;~6e7i$dioeP|O`J6qF%Te-=dO)xoe}q45l_6n;Ug=RG zQFwT)00YtOV1XKt0zICCUo$#KdIuU~N$3WfqGwfNaTe86SCHG#)pb{p8f34wQKoKx zrxkQGYX%CbIB;#VZQL~3%uO;fx1xs~`fG(3|Mur5X*@(B*a?ioakYx5242}N`v1*< zwMWgSMk4aO1Y9AxIa;}mkOn(rU@KQrofGIsLd{z{D%8tYSh#~5yp|ChdWZ-IU>pT%4o4iSdrdmMqPIIz2({a8I BEv^6n literal 1769 zcmV=18lNgP<+wkyc4!W-gj~{$Uk8#>*j5c|Nd6P zRcW=X`QVzWop=Wrm#Wb-YfT+Lp(JhzgOe=-bgRPgfaCSnC*=xoo>bqQEX~b%jZ46^ zLD|~9lv?Tkg;4pIz0Bfj;zEmxF9E@cJ0u-^hUmZ?F_n&}rM9@BdlCy0G=l7G#6Q-I zYGc2qWg!ta9lCti6w*p-OnD+eG~~3r+j=6$dfmU*Z(v$N$`!R;lyJE)tVrb0BXQV| z(mbh;PVLa@-HJ^E{I+@0zK6SaW~>*^E;Cl}FTL<&0AW-HdW^|1cEknA!0Y^bX>H=G$Kq9S5zfw#(cB4Ru zw>+f*`DCU2j!G0zwJP;@-7{VH&JNk77(+x+qx(emE*8X5JteLEaKJe+OtsfIjY~zL zoS=9P5i0K`430xr_33^yQcw{5smkHu_p8UCjX!QV?(1X=XrqZ*_8GZDTHSbv}s!)C3a&2mn47 zAp}<-?wlY|@3mX%LTe7|{y6hELGlFxU#Y8V0viJb2?N4s00j#P2nPZN6$l9m3jzcd z0s{d89svRufB*^!5Dx49IP*9`^4rJ@|3e#9LoR1eYYJQeK%OPt++byMf}e3?XPS&9 zgr~J&XEtpPi3j{Xuk zODnKA8m{Qa4Zz17@0|n>1$K^<8l~?^#lNSu5o;$K;qQz!Ytbq6tAj7M>y#PO8SKN+ zq5;X*zN}S)ppTF77zSP|wKhuyTBUt{S^KSrp`_Y`jDVwPi&1g~L^?xH8%+d+{dqFS z!uAIsxE}X@c|mvu>XzneS1JfsSns)BC+eUBBmMeF)s%!=0H-d?Dv^k{_6G&F;;u|{ z%iV{oH{+Snc1+kRg$b4TV@P67APAS1vKMuSuSi%9`?1oO&In*TFy3=ZWw`;31YfDE zY5@!Y&>jYqN4-tdvgIiMd- z`B1)<0sx=J-*Cjb?t9c0c~-z%TF#HZl9z+LunH~kS()8V_Tw!^<;B7No@uQl7vByx zVL{r+=^o=Oa{F?kI1I7^kIDW6fMq+K&^B{=qxIDv|5aA_z*D~4|7Gu{us|7T>EJmx zFiMC9+jD}si}O$UXhGg@NNRe#g5(kqc=X<;T~_DsI@BXoq`3{BY(9@pC#05f&y-cXYdyRfD&)?yDoz8ZGnmGNfANB|sD>Cy0yNS+wlL9+ z6y$CG7fKzQ4dISvUkYNzSfv6r6&1Z{01*KI0f_;;1Q-Db045e81Xm#LoFGx}wOi^! zYYyxFIP*9`@&y84sjF%N8w>>r1Hxwj0162Z4(t9n^Eg5B!6Xa-nwN8Yg8BPlv9B(G zxGf^YY-4jTwvaQRd3ib9amtaucY%rW@U=(~a9ZZJ*gwMUSay*o1od}$G?Dc1NPM>E z!8AEH0%UKXsDJ~gTz>4cJmm+GNKx$8HFrDH*8iSA7>iANHC87eHR}ofa&hty@*@F- zve)}U&wI3c=9lDiIGsVTZx}^7&vtLafa*uJQ|j`!D9*3QonJCs;bKb0+W@sh-`79) zV>4XZNP=tkY9=f