From a4f890c8b29157c1635d52954600b93dcc01b7f6 Mon Sep 17 00:00:00 2001 From: Stefan Prodan Date: Thu, 11 Oct 2018 11:16:56 +0300 Subject: [PATCH] Add autoscaling support - add HorizontalPodAutoscaler reference to CRD - create primary HPA on canary bootstrap --- README.md | 118 ++++++++---------- artifacts/canaries/canary.yaml | 12 +- artifacts/canaries/deployment.yaml | 4 +- artifacts/canaries/hpa.yaml | 19 +++ artifacts/flagger/crd.yaml | 8 ++ artifacts/flagger/deployment.yaml | 2 +- charts/flagger/Chart.yaml | 4 +- charts/flagger/templates/crd.yaml | 8 ++ charts/flagger/values.yaml | 2 +- docs/flagger-0.1.0.tgz | Bin 0 -> 3136 bytes docs/grafana-5.2.4.tgz | Bin 6838 -> 6835 bytes docs/index.yaml | 24 +++- docs/podinfo-flagger-2.0.0.tgz | Bin 2887 -> 2884 bytes pkg/apis/flagger/v1alpha1/types.go | 3 + .../flagger/v1alpha1/zz_generated.deepcopy.go | 1 + pkg/controller/controller.go | 10 +- pkg/controller/deployer.go | 46 +++++++ pkg/version/version.go | 2 +- 18 files changed, 180 insertions(+), 83 deletions(-) create mode 100644 artifacts/canaries/hpa.yaml create mode 100644 docs/flagger-0.1.0.tgz diff --git a/README.md b/README.md index 28da53ad..a0982a74 100644 --- a/README.md +++ b/README.md @@ -45,25 +45,25 @@ Gated canary promotion stages: * scan for canary deployments * check Istio virtual service routes are mapped to primary and canary ClusterIP services * check primary and canary deployments status - * halt rollout if a rolling update is underway - * halt rollout if pods are unhealthy + * halt advancement if a rolling update is underway + * halt advancement if pods are unhealthy * increase canary traffic weight percentage from 0% to 5% (step weight) * check canary HTTP request success rate and latency - * halt rollout if any metric is under the specified threshold + * halt advancement if any metric is under the specified threshold * increment the failed checks counter * check if the number of failed checks reached the threshold * route all traffic to primary * scale to zero the canary deployment and mark it as failed * wait for the canary deployment to be updated (revision bump) and start over * increase canary traffic weight by 5% (step weight) till it reaches 50% (max weight) - * halt rollout while canary request success rate is under the threshold - * halt rollout while canary request duration P99 is over the threshold - * halt rollout if the primary or canary deployment becomes unhealthy - * halt rollout while canary deployment is being scaled up/down by HPA + * halt advancement while canary request success rate is under the threshold + * halt advancement while canary request duration P99 is over the threshold + * halt advancement if the primary or canary deployment becomes unhealthy + * halt advancement while canary deployment is being scaled up/down by HPA * promote canary to primary * copy canary deployment spec template over primary * wait for primary rolling update to finish - * halt rollout if pods are unhealthy + * halt advancement if pods are unhealthy * route all traffic to primary * scale to zero the canary deployment * mark rollout as finished @@ -80,19 +80,27 @@ metadata: name: podinfo namespace: test spec: + # deployment reference targetRef: apiVersion: apps/v1 kind: Deployment name: podinfo + # hpa reference (optional) + autoscalerRef: + apiVersion: autoscaling/v2beta1 + kind: HorizontalPodAutoscaler + name: podinfo service: + # container port port: 9898 + # Istio gateways (optional) gateways: - public-gateway.istio-system.svc.cluster.local + # Istio virtual service host names (optional) hosts: - - podinfo.example.com + - app.istio.weavedx.com canaryAnalysis: - # max number of failed metric checks - # before rolling back the canary + # max number of failed metric checks before rollback threshold: 5 # max traffic percentage routed to canary # percentage (0-100) @@ -166,50 +174,68 @@ export REPO=https://raw.githubusercontent.com/stefanprodan/flagger/master kubectl apply -f ${REPO}/artifacts/namespaces/test.yaml ``` -Create a deployment: +Create a deployment and a horizontal pod autoscaler: ```bash kubectl apply -f ${REPO}/artifacts/canaries/deployment.yaml +kubectl apply -f ${REPO}/artifacts/canaries/hpa.yaml ``` -Create a canary promotion custom resource (replace the Istio gateway and the internet domain with your own):: +Create a canary promotion custom resource (replace the Istio gateway and the internet domain with your own): ```bash kubectl apply -f ${REPO}/artifacts/canaries/canary.yaml ``` +After a couple of seconds Flagger will create the canary objects: + +```bash +canaries.flagger.app/podinfo +deployment.apps/podinfo +deployment.apps/podinfo-primary +horizontalpodautoscaler.autoscaling/podinfo +horizontalpodautoscaler.autoscaling/podinfo-primary +service/podinfo +service/podinfo-canary +service/podinfo-primary +virtualservices.networking.istio.io/podinfo +``` + ![flagger-canary-steps](https://raw.githubusercontent.com/stefanprodan/flagger/master/docs/diagrams/flagger-canary-steps.png) -Canary promotion output: +Trigger a canary deployment by updating the container image: + +```bash +kubectl -n test set image deployment/podinfo \ +podinfod=quay.io/stefanprodan/podinfo:1.2.1 +``` + +Flagger detects that the deployment revision changed and starts a new rollout: ``` kubectl -n test describe canary/podinfo Status: - Canary Revision: 16271121 - Failed Checks: 6 + Canary Revision: 19871136 + Failed Checks: 0 State: finished Events: Type Reason Age From Message ---- ------ ---- ---- ------- - Normal Synced 3m flagger Starting canary deployment for podinfo.test + Normal Synced 3m flagger New revision detected podinfo.test + Normal Synced 3m flagger Scaling up podinfo.test + Warning Synced 3m flagger Waiting for podinfo.test rollout to finish: 0 of 1 updated replicas are available Normal Synced 3m flagger Advance podinfo.test canary weight 5 Normal Synced 3m flagger Advance podinfo.test canary weight 10 Normal Synced 3m flagger Advance podinfo.test canary weight 15 - Warning Synced 3m flagger Halt podinfo.test advancement request duration 2.525s > 500ms - Warning Synced 3m flagger Halt podinfo.test advancement request duration 1.567s > 500ms - Warning Synced 3m flagger Halt podinfo.test advancement request duration 823ms > 500ms Normal Synced 2m flagger Advance podinfo.test canary weight 20 Normal Synced 2m flagger Advance podinfo.test canary weight 25 Normal Synced 1m flagger Advance podinfo.test canary weight 30 - Warning Synced 1m flagger Halt podinfo.test advancement success rate 82.33% < 99% - Warning Synced 1m flagger Halt podinfo.test advancement success rate 87.22% < 99% - Warning Synced 1m flagger Halt podinfo.test advancement success rate 94.74% < 99% Normal Synced 1m flagger Advance podinfo.test canary weight 35 Normal Synced 55s flagger Advance podinfo.test canary weight 40 Normal Synced 45s flagger Advance podinfo.test canary weight 45 Normal Synced 35s flagger Advance podinfo.test canary weight 50 Normal Synced 25s flagger Copying podinfo.test template spec to podinfo-primary.test - Warning Synced 15s flagger Waiting for podinfo.test rollout to finish: 1 of 2 updated replicas are available + Warning Synced 15s flagger Waiting for podinfo-primary.test rollout to finish: 1 of 2 updated replicas are available Normal Synced 5s flagger Promotion completed! Scaling down podinfo.test ``` @@ -260,43 +286,6 @@ Events: Warning Synced 1m flagger Canary failed! Scaling down podinfo.test ``` -Trigger a new canary deployment by updating the canary image: - -```bash -kubectl -n test set image deployment/podinfo \ -podinfod=quay.io/stefanprodan/podinfo:1.2.1 -``` - -Steer detects that the canary revision changed and starts a new rollout: - -``` -kubectl -n test describe canary/podinfo - -Status: - Canary Revision: 19871136 - Failed Checks: 0 - State: finished -Events: - Type Reason Age From Message - ---- ------ ---- ---- ------- - Normal Synced 3m flagger New revision detected podinfo-canary.test old 17211012 new 17246876 - Normal Synced 3m flagger Scaling up podinfo.test - Warning Synced 3m flagger Waiting for podinfo.test rollout to finish: 0 of 1 updated replicas are available - Normal Synced 3m flagger Advance podinfo.test canary weight 5 - Normal Synced 3m flagger Advance podinfo.test canary weight 10 - Normal Synced 3m flagger Advance podinfo.test canary weight 15 - Normal Synced 2m flagger Advance podinfo.test canary weight 20 - Normal Synced 2m flagger Advance podinfo.test canary weight 25 - Normal Synced 1m flagger Advance podinfo.test canary weight 30 - Normal Synced 1m flagger Advance podinfo.test canary weight 35 - Normal Synced 55s flagger Advance podinfo.test canary weight 40 - Normal Synced 45s flagger Advance podinfo.test canary weight 45 - Normal Synced 35s flagger Advance podinfo.test canary weight 50 - Normal Synced 25s flagger Copying podinfo.test template spec to podinfo-primary.test - Warning Synced 15s flagger Waiting for podinfo.test rollout to finish: 1 of 2 updated replicas are available - Normal Synced 5s flagger Promotion completed! Scaling down podinfo.test -``` - ### Monitoring Flagger comes with a Grafana dashboard made for canary analysis. @@ -331,9 +320,10 @@ Advance podinfo.test canary weight 40 Halt podinfo.test advancement request duration 1.515s > 500ms Advance podinfo.test canary weight 45 Advance podinfo.test canary weight 50 -Copying podinfo-canary.test template spec to podinfo.test -Scaling down podinfo-canary.test -Promotion completed! podinfo-canary.test revision 81289 +Copying podinfo.test template spec to podinfo-primary.test +Halt podinfo-primary.test advancement waiting for rollout to finish: 1 old replicas are pending termination +Scaling down podinfo.test +Promotion completed! podinfo.test ``` ### Roadmap diff --git a/artifacts/canaries/canary.yaml b/artifacts/canaries/canary.yaml index 21b71b76..5f901c9e 100644 --- a/artifacts/canaries/canary.yaml +++ b/artifacts/canaries/canary.yaml @@ -4,19 +4,27 @@ metadata: name: podinfo namespace: test spec: + # deployment reference targetRef: apiVersion: apps/v1 kind: Deployment name: podinfo + # HPA reference (optional) + autoscalerRef: + apiVersion: autoscaling/v2beta1 + kind: HorizontalPodAutoscaler + name: podinfo service: + # container port port: 9898 + # Istio gateways (optional) gateways: - public-gateway.istio-system.svc.cluster.local + # Istio virtual service host names (optional) hosts: - app.istio.weavedx.com canaryAnalysis: - # max number of failed metric checks - # before rolling back the canary + # max number of failed metric checks before rollback threshold: 5 # max traffic percentage routed to canary # percentage (0-100) diff --git a/artifacts/canaries/deployment.yaml b/artifacts/canaries/deployment.yaml index 895dadb1..3a89acda 100644 --- a/artifacts/canaries/deployment.yaml +++ b/artifacts/canaries/deployment.yaml @@ -22,7 +22,7 @@ spec: spec: containers: - name: podinfod - image: quay.io/stefanprodan/podinfo:1.2.0 + image: quay.io/stefanprodan/podinfo:1.2.1 imagePullPolicy: IfNotPresent ports: - containerPort: 9898 @@ -66,5 +66,5 @@ spec: cpu: 2000m memory: 512Mi requests: - cpu: 10m + cpu: 100m memory: 64Mi diff --git a/artifacts/canaries/hpa.yaml b/artifacts/canaries/hpa.yaml new file mode 100644 index 00000000..fa2b5a6f --- /dev/null +++ b/artifacts/canaries/hpa.yaml @@ -0,0 +1,19 @@ +apiVersion: autoscaling/v2beta1 +kind: HorizontalPodAutoscaler +metadata: + name: podinfo + namespace: test +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: podinfo + minReplicas: 2 + maxReplicas: 4 + metrics: + - type: Resource + resource: + name: cpu + # scale up if usage is above + # 99% of the requested CPU (100m) + targetAverageUtilization: 99 diff --git a/artifacts/flagger/crd.yaml b/artifacts/flagger/crd.yaml index b3115a56..705f337e 100644 --- a/artifacts/flagger/crd.yaml +++ b/artifacts/flagger/crd.yaml @@ -31,6 +31,14 @@ spec: type: string name: type: string + autoscalerRef: + properties: + apiVersion: + type: string + kind: + type: string + name: + type: string service: properties: port: diff --git a/artifacts/flagger/deployment.yaml b/artifacts/flagger/deployment.yaml index 2f694fce..9cb278cc 100644 --- a/artifacts/flagger/deployment.yaml +++ b/artifacts/flagger/deployment.yaml @@ -22,7 +22,7 @@ spec: serviceAccountName: flagger containers: - name: flagger - image: stefanprodan/flagger:0.0.1 + image: stefanprodan/flagger:0.1.0-alpha.1 imagePullPolicy: Always ports: - name: http diff --git a/charts/flagger/Chart.yaml b/charts/flagger/Chart.yaml index a742bd98..7651ffc1 100644 --- a/charts/flagger/Chart.yaml +++ b/charts/flagger/Chart.yaml @@ -1,6 +1,6 @@ apiVersion: v1 name: flagger -version: 0.0.1 -appVersion: 0.0.1 +version: 0.1.0 +appVersion: 0.1.0-alpha.1 description: Flagger is a Kubernetes operator that automates the promotion of canary deployments using Istio routing for traffic shifting and Prometheus metrics for canary analysis. home: https://github.com/stefanprodan/flagger diff --git a/charts/flagger/templates/crd.yaml b/charts/flagger/templates/crd.yaml index ed4594f6..50f02017 100644 --- a/charts/flagger/templates/crd.yaml +++ b/charts/flagger/templates/crd.yaml @@ -32,6 +32,14 @@ spec: type: string name: type: string + autoscalerRef: + properties: + apiVersion: + type: string + kind: + type: string + name: + type: string service: properties: port: diff --git a/charts/flagger/values.yaml b/charts/flagger/values.yaml index d296bd82..b358e1f9 100644 --- a/charts/flagger/values.yaml +++ b/charts/flagger/values.yaml @@ -2,7 +2,7 @@ image: repository: stefanprodan/flagger - tag: 0.0.1 + tag: 0.1.0-alpha.1 pullPolicy: IfNotPresent controlLoopInterval: "10s" diff --git a/docs/flagger-0.1.0.tgz b/docs/flagger-0.1.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..cc2283d9e50a7480a38998600cd4861a4ee0c19b GIT binary patch literal 3136 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 aQ*6t&Y|9^`{9gb70RR6$CXGt~L;wI7>qhth literal 0 HcmV?d00001 diff --git a/docs/grafana-5.2.4.tgz b/docs/grafana-5.2.4.tgz index 53e83c7a03f0e455767a098b8cbc7ee3a473bdce..218430a076f254d773c14676b2e6aea2b227e4ab 100644 GIT binary patch delta 6822 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 Uck%pR00030{}ZQHgaGyc0PSH}o&W#< delta 6825 zcmV;a8dl}AHMTX7Nq^h&5XTOLm#ksdVt~%WdRodKXlKo7dlJP~5)HQHk>mhFGuLoF zobTrr&n28C`Av2b0)+x??X^l1y=+Na`{k!C$r&;|3{09o$#)poqM~ zj1wAmVC)zuit?{*t7X}Shq=p0BvjHi{0sX5xKdbPKp6xyB!V&$4k2Q6$S~(*jKRZ^ zPcH%-3JwuH!wh@Szkq!%2sHpu=)p@L4TqQ+BO2fijD(1Ir`a45F^c<^OM@mCc!0u) zQ4fX9L0URQvVU|cw8mKP>Z+2HCgmbm^Jb>* z00Ip&We132-#?^2aW6WsKRBS`kYSEPVHg~w_=aS`DT?D#PknC&0RYLtl%Y}m3F zoW{(>yaSgL1KKwdyXLo)M>GB5jXJV|UR95NbUF~Tti z5^{k;7aKA26NB1mHlsYhvuSVIvf^(TjWO)_H1_g3Am}L$4fVHE+tKgn$#g8&&gQ|J zlf7e0oQpeaV}bo|t+duF_J4K7sqx>vJdOpwVt)Z*gaJj$C`*F`Z;xI{z{cV*l-N#; zFvsB1AV8tVJI3W@1IPee8VW>04(0*%@F8Ww1nXUbBvKn}NdpWMX*@2;xQ|`o!w?H-G$gp=5ppqHUV?RmeT+D^4)WB=1R50n;53Yo7;TxC7LTxN zNq?&Rz5US&(@soGNL%NUQYPA9W`1oD`@U?<1PdmU7nQ#4A69xM>GIF&9}zfEUE>0Y zm|Mv6Bt*t4E&VbvRR`P=&5ZWbv>TnLIj?o|)=rjuGscV&55p%AEDl{*TTvfLa2yW? z;~fy}dmQ9qSFIDVCvBFH3*_hh3&o4wgJW4%Iz0(ZURcKd08$g05XsGb5<^pIM8FyG zed%Xoxn@Rkxh*@N_I5673;&gIgbZ?SW|=uhvodoFT#Mz>ZDn7cs_u8Tu0N|@+e}C}CKe$<;mb7~lIh*&nB!Ak(N_9zNlyXwl0SP@E3TV|R{$8G0`Y%u#7r!F|uxS6MwN{P) zt*y13TL16k`9k`ii;$7k*3y(nb>LG&#ggynlx zJl$XGNc@GtN zD$>&mdA-6DSbu~zxyz9B56=e3=lGc_m9bS#XcFnRKSI1scL+Vpb^~YUf6#>J*1c%6(9qj2(UREAP6*0;LRXq=)LT$%<{;l@bx7 zg1Xf2z{$>`@ubM`$ry*2^Fv1axTEKQYUx)jDyaYwl7ASXIhCv$SKZTc7)Bns`xc5s z9FBEHWo-KOW%qb*=k3w{$?x6m-PijE-M7bkMY`z%eUN#C&px+sDVhy*b)l zKr%Z^Gnee)_Q}7FyN5?_-t8anzd6`H_;nV=CKhgXGD!lyF_A`;gu|v*dQAN|z^|p* z;InvAoPTU8oTTY|_lB1T1gT059k$VBP5mfl7|b3-SY;?kKd zWaqjxW15}xx+P`p2w6c~KUa1$`kBB^t+%T3hkt`eTXt2430|cbw;ohM@*(lxH2%+-a982qh4J6E)2haQR-8Kib1%;qiT@OOl-12sve?f* z=f!^VlI2juvviB`S*mT64fnkwR5GBxhgk=nJu5{j2E@Hqp_HU8Uf)%t%w&rJPKm&IRr$^R&p`-z>H zaaON5a;Gi*8tIDXinD#G`9W-m#bI@MR9^Mzm?14x4lCSf9e*WH=FWen|JDfm0U3sr z;X9YH#s0s#|GQ$>=fCggc>;$>2+TrtZ+|Bd05}`r5c)CkJ$11Uxua{CULifShA5S<(FX!h$!Ue*rX9 zHD#9|!VG*8V#~00kGsc$GHg779U25Qgm*i~;1R|RYe+;>{Y|=WSpC0PQ~k{nMt{Sm z{F8m<Ld3wjuckl#*)R)B4f$wqf=wa5SvPKfXo?km>)?-uCY6JuC3OKpTtV|MtpS_5QE3TKoTddCK>}eofc;M*0E9 z?L;)H%G@;iqj9#zj(vVXh8&5%cu$&NAcs|4F>6v&aYQu-bp*J`dp!@u->?%vC zgxWjZ`fYFf-CpD5&A;~!O1W7`aVxo<*X#8R<7AXym`Sfg^NhVfLp3susef*A_8&-+ zlbxr`5As`z-uooz5GT={kI$c!zP|vDU1eK+!Ub1^BE(RD1!nRq66&@_A466Td*H?D z_IAoQMJSo8w;`D~TXB}vOm^y3wmvo51@M`(lvZmrqL>Z!X_@97!vP&*4w-c5Ort?W zLv_g#PMcb8h$j5@*wc(An%3 z?TQg5ET7EuinU=JW>w)6>=xJS)i+htXfzt|3G%;2ue#sSgHNz8)3gh-@CkaAE3>_< zX0feN)RbuuC3_v8pr>D<&Fcv?EGh^zlm*+CZ8<%or*DeMLLTNsN`F)s2Yt*WSJZWs zPtZ$Br@OTxuR}BsfwJFVJKx+R=k|_jGk^FWHq%opo7eC$a4CLxa+UobG{SdA-_MP-m= zt1Lb2+*2t%qnFvN>C*WINlw-{j<8Dx7l4sFDkL9bp4o<8VhB(1#g-ED_sjXmk3B$a z7%P4X3O(IS_bhlx8Q^miL_S`=wLw?C*qUvu=bP$nU!qL~T7UhE<@Bn%!h?y?sxO$bZ(9xX0|k`!q~-nTNs6r%y+SkN%A>%nq2eFPFj{r@gUmE*E9`G4aKI zXm-HSQj1+LS+&u~Jx`;dSH__8ho?o;*)a!-eh#-Z%7+E*&)tA9}O>@2RS7^LFcT5T^wdo}rITbm^T zPD-q-FN4$CT!vL!7TK6lrfn~Sv))<;NzJx4*I#5JT=1`~j|Ju|7?-^8M=5%wB@azAYqoFC-V_KtMWun$B1D*qP~|Lr@N}eYs5~m;-I!`H(4y3s}7rD=T5K%bd#GzI6EHF?jrChIvFoj(@u@_3)2> ze=ytDQUL^@36G|$S)aZbDEZ|o=`G`r&Gs^EZpwdFdj=NCA;@v8?tj7&65k5|LF3`$ z03UU%;9+A0PV1W3V!g%|HMY1XY#|lB)mmMKwf5&hizn;2-&*s&vj{=5@%42F2zJyM z>i9q%AE@I4aBU~PaZM+_S${k6+KJar{8~`~$9|kqfy~Q4{-8h=kJK@NM-4jW#srX0 z5Z}q*!DXRxeZ;05o;s@W3Wq~6QkJ!pj%O8LxGX2iR5&;HlcmM&tex@oIev1j@P~Ma z6^_!HLOvOW+x#RwHdFQYl8@7=8Kj%e84(Zf1(CRzllLE((HU6*I)9&)O2Qebj0{KO zc;@M&d9X&Bh#Be08Oym4MjCKlJ)t5K&xn@@#8pUR%J-JMR|f6Sa2mWuvKB)gi6zN) zIel8_7!Ezmu(Ia^pK6+#n4#=_&z1fui#JQQxj^peR4Z~l6k+dG;w&rH;<_u#5#fRk z849|8#wZlT$Iop`LVxl(*1hYc*8#d5yEOE8w;#KwSlrCqSK`lbGnX)V?*!5X^)KML zk8ff4OO70_;Sv&ceVMm#*sgeSZSA@cZC*JAo8R~pq-PZ?`u`a-usLrAe!1-oY}PYy zy&2e?GXp!<#!Z?Ll>L2~tS(B6ADMnUB7><_(e&YuET*%Y&VP~yu<|3Om%58W40|(N z7iFU2)RlYcW>AcT9vKraMmY$YOu;pEvbE^IIXWj5WRZwYHRPb3-jpMsaYnaNT3fr! z@~7`VPId7DonN)GE<~szIO;yoR0m`R9sYtio|2dZN=y|`u*6jI^dfA&0&9I7$=P~< ze(FF)lk;yJ>3>e`>&%0?P7QTysQcYR-8`sLBi#o=x(_x3HPHQLCg73a+-wiChPmYq z*D&{yz}#EjvU_{H_fWU&9A`F+b?htA*8SYG`)XDXYl~J7Q*2+I$9DT0UOrUwW}CKp z`S3Z*htjZbwR)I&n|p1eJ%9IB!KnNd_gy!NyD>*YoPT4eWPj9+;;j$K^nNgx3;a{r z{)}h>j`V1uwIp#=(S*$Z@$V1j;hWu03coefPtBCUQ%30F5Y*)*WSxM?7!{ zG~79S3vcB#NoE~VuSQd!gQjL`%f@PZp3Kd`T&A3jR9&E%eli~zh0Ph;+T5{y4Os2l z0Tojr$$$B%qE(}c8dcP&B10AF8;xpEan})=8dH=zTw{tFQ+!pJq8g}Z&3O%zW3SYZ zqJ|V-8KfwDeSibXF4S%e35Q*dMYn$;Fu(jlVTmse9&Wwg`50kE9;Xg-4DUU3{u#3qUSK_U1eiwhR>1_@+7awGu z)9mJ)X4QGCfQTy^Zf!c7&Vn}*+}lz0nXd|HZ*E+@Ed(0E3bByrs+%%Afmi9?(2avR z*?&&YUEi#)qM+H*ag!6~Gv%UHb+H-a8axhQF6%O(xf9-fY1Yk?@CT1k9VFa@{pOn* zH9j_nFKEL^E0cMx4zEW%6YYOtWnD#H=d7`{Ijb*4L6rGR+wU?rP{u|Km@bG)3vwtv| zSktM+5MVNa4qRTAM^?43^v%UI7#OlXdt{#_M z&0U`@5T<)rc+CB^KrZL+SPd1nLl2J9F~N>>7S~*xOgX=AYH^}ZUgE&>l)G8CY*t^l zGW+Lq;O_CfS$WaUZ5Jmk+qS>z#ed1S1D~gVj%W^k-du1+w8qaheul-r*7L}aaq$kW z8o)dPVEp(YZ!Iw27UG@v^H(+Cy;s2dRY2Jqq&|`u@9LbF(ycFeIa-ZTYm5r_g;D3a z$!WAvDSAb0Eh$+689G?un2pIDqKTV>h8ey2mXPGPF|Bt95^pl!j~EE%p?}Xeu^7@S z65K5YS;N5^4nhqFA5+Y79v<9Wa5uchgEbz6uLKXy3_R6v@GFOdk7HJAG?-^p!h$sz ztij+n0S1j!U!B3a1i-O zkZ_m_nduWQw7D!@c=EH~`G2oSQvtb3Jk5S?*~0-XGc^CbWtBB+auy@f!tUuhM?%++ zJ-qGD!eaS}?}N;)>(qkS7i31Q>^JnoS(Ruc%}Cj90hoWrn5h#J>O+@d9{VEkTQfY? zw1=4XM=yXfCT(>Gu4i0}OzB7E3t@6fsY9%OU%zQBb@eMB&wt!w0tjj4ly}{1t)e4Iv#mv- zGPt9@tmt2yp!nR@K0ErC6pojGhffqwU?D{%B;&2V29@&VMuLKb{J=Q=tu2&-xGU zt39|ULNsUZQE4Vc2-5+fc|N}!1jArJV$_jhxp75Dezm}_$TNuSQh|w7@Uve$kSm!e z)S68ZV&rU!YDQ2bOkfC!G&v`!%rJ!1m|TawUcyXTjDtvKJ#BD8MXqE_MeqE%?v^W) zWRwX*nLwpO(ticaDB~g2+5b^!R5}n+Va{j4LMQ~BCcdb(@QId%iqLhjOuMSd zw6fGN=m&lBcTeRPQQQBglozNUHvla0|3QDa-S+>j-O<|rS3w)Fj}uyO1AgY6KerY| zKnTldf*}D=m`lx!RI?ENDCjI;vgsU^+X5JxgwU~3q<`NbWCKvpF-I^dLP8B*&>38$ zC;%_5NRhZ2jcUpw1X&o(V$U7%LK{@1Y-QE(vf>aXlxv*(d0vb;i@GIaz_ekqt7qC^ z7U*ddl#&82BgzTD2H>SZB`Akj+Cbyv5P(!_6B3f<)b^G!af~@<5!E3KZ2`Sp6l}S% zssznXQ-4%6!FPsn_L2IK`Qim3hGi%V6T+yk375%hoFWgw*(s7`bUTvDgz)0|i|5|j zM+N6clCx+Q!olRNG)D?G3PXs(d9VWDEMvy~0*LY=gkitm&)j8(nVYBW!SD@pbN5Gq zy1_hX^X_cDVNWxXt?5gs9sdnxIj077@9p8~tACTgT$%^>u`K@^4BGL3dv~4xt%l&! zC#dEeI_%2IT|r7Uj1dLIm~%P97<3UusI}#reYcJqPmgZ*{|_nh+~(CL ze_$%O#Q%qb;h^RJJ3GU*|F48Ted=w(lw~1kG%#Tt%`C^4nKet4VhEePt1Du;$g9ho zihtN;WHu&%ocQd_AqJ%6>q3kZCT@r&K8|!3TrTSjJmB!w3Jl)+&4=YYoT5@T0sIq; zDntZ3BlnYKC&gsKE}_$12tyIO-(Jr>g*KScc^@u1*=&`w3Bwp@p1TnQg*WK@Rr=>z?&VJ9ea=QW%75C`B;{M{L>X8(;EPt$r zmAHak8g7Y{i^_9xI!1nnW#YJRA3@#i{O=O?fj{jYkJAXD^M4)ae7NVh{_cRbrhC!t z_kX{tyFS&+P<%NmyyX6G@BGdC|FFL^+FjrOS3!0Bm*%9lITOjdJf;SHfAtd2>R_UGf=(&pbj&e?z7>ZY zBO{gP$*76a*EIv(#Y8RpR*a>@+FV)U4dGuP-Zc_J8V6|AB;`tGXi_Y+1AhS{MXhN% zMitm`OqB(l-B4vrI1#@n8sQNtCgT%~q=^t=(o(x3G)XILoMr!&;G5IjA9__3~~vm&&}XNpB#4 zP1(Ky$0_o9iwZ()GNYK*m4B$Lc67LZ@b>4!5AO~>>>a*7JZ^}0pl=9?LKL+MtDw*J{9T@zXQT0-!+@v3CtrgOkJ*6R7g=Zfdjq{d^)_*Fek{21ik%ee3 zlWLLJ0Mw9`S=J+J(hz#B${Lz&!;<_L&G=l;c_sV_OvES*42Fw^R>JI*naZ(NAdOB^(ucg$s@ctHS97NW_xpmT7UCCTOq---U z>PlubibZZRx>b@}#$Hm7(p9MSx<`3Y<#)FpfVO(RjnS&{cRRy3NK4~?c_9Cgd%&{u zzundyAOhU{)v$TyFIDxihsK9;x2m~AMgCmsFt7K zwDoChu$nl*J$LPQpa0wazeLp^0RUWz|BYJt-*C|18m#?)74%o&fAjO0uOkrr?*V{q z13xwb*p?4Z0RsHo(}>|97zBeg+*iVVV_x7I?OUUL^`>jI@0+CM`QO67bC|4OLt{{qd0ROd`2{>{mN z$=-B8d7jcy85!RDMe&6J;+l&;@d#n}+4BQ|`>5V}YJb0aU$uOzwbG+ok1rLE?b9=q zC1pUmfmM`as-Dkwt!$L>EtD)O$gm4BYEy!9i!Hvx5$+Ea`aoGn3Ph2OQQZYBxlC=&dy@%br#m+m8Q1iacY9kP08Ik7fDa_O<7Kwzjsm*YST9 zv;jxd7=KhkgOT7T-oKVVE*OuQNFb-tIZaRpWCKo9ra>3E%Y;Cu$T=ijj{P|X6UqPE z@JbDvBIMMhwQ?$AvH=1UzuEgCS2$sp7{diKDSY>10EdFlKnmB=ssuSI;7njZg8h>Z zCq^nH8?Yy{ObU3vcLFg}ngj_mJ@?nEPlEAB)qit;tAaG?*+12#o{HW=(3nQ&Meg2x z(_}Ny7dhDs#`K(Q1}4kN=6}cryr+uELc_uSD@}r2$=@+DBw#Vpp665Y_b24`b87ql z@vEQq-@FR4_!0Y9;{U^8f7tT>(e}>T|5rjA^Kbeb*p!cu>XK=oV3?S}1&(cU##!V& zH-DosdZ+zG0x>Bypi_AfQH>hLs14+bMutVGK~6vwLf9~pu}H~sGogWr+Iy7&AsZWT zAhehf6ngP9qEnIXgRxq|lNhIWNW-84i(eHs=C^+RSTR z-r1@I|FsY%tZOqVUY0#s>DSfxrTl^k6Ms(Gv-+n-&T5kFuNcmnA0|2TI@=u;xn}}i z_IpF--Rz<)_7;M=b_@8jvC(uaAu!>-;IxopXRJJm?KaYl?d1xl`Ynj4_0hgEvR7Uw_TT zuqie88_q)Lp^19VO1-9$x)Ca)dxq=a9A{-qZ?-*DhE1&F;N1j7$~4xSTU!$)g+URY z&EFs%&)ms6o6IV2x#>GLP0z529Ta(@tov3Xq01oXxgC=3weAJ*Z)^JQd>OrTUg8Ti z8ffiWUfRjJq1u}LWhpwqOPb{zgC0m`;0O8?Z<=db)0)=w70`bJ00960@a21r08{_~ DqH?^X delta 2856 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