From 484c8db151690a4ae7b6b0ae38db0a8ede88df69 Mon Sep 17 00:00:00 2001 From: Carl Lundin <108372512+clundin25@users.noreply.github.com> Date: Mon, 30 Sep 2024 13:50:37 -0700 Subject: [PATCH] feat: IAM signblob retries (#1600) Add exponential backoff w/ jitter retries to IAM signBlob calls. --- google/auth/iam.py | 29 ++++++++++++++++++------ google/auth/impersonated_credentials.py | 25 ++++++++++++-------- system_tests/secrets.tar.enc | Bin 10324 -> 10324 bytes tests/test_iam.py | 13 +++++++++++ tests/test_impersonated_credentials.py | 18 ++++++++++++++- 5 files changed, 67 insertions(+), 18 deletions(-) diff --git a/google/auth/iam.py b/google/auth/iam.py index bba1624c1..1c2eb913b 100644 --- a/google/auth/iam.py +++ b/google/auth/iam.py @@ -23,10 +23,18 @@ import http.client as http_client import json +from google.auth import _exponential_backoff from google.auth import _helpers from google.auth import crypt from google.auth import exceptions +IAM_RETRY_CODES = { + http_client.INTERNAL_SERVER_ERROR, + http_client.BAD_GATEWAY, + http_client.SERVICE_UNAVAILABLE, + http_client.GATEWAY_TIMEOUT, +} + _IAM_SCOPE = ["https://www.googleapis.com/auth/iam"] @@ -88,15 +96,22 @@ def _make_signing_request(self, message): {"payload": base64.b64encode(message).decode("utf-8")} ).encode("utf-8") - self._credentials.before_request(self._request, method, url, headers) - response = self._request(url=url, method=method, body=body, headers=headers) + retries = _exponential_backoff.ExponentialBackoff() + for _ in retries: + self._credentials.before_request(self._request, method, url, headers) + + response = self._request(url=url, method=method, body=body, headers=headers) + + if response.status in IAM_RETRY_CODES: + continue - if response.status != http_client.OK: - raise exceptions.TransportError( - "Error calling the IAM signBlob API: {}".format(response.data) - ) + if response.status != http_client.OK: + raise exceptions.TransportError( + "Error calling the IAM signBlob API: {}".format(response.data) + ) - return json.loads(response.data.decode("utf-8")) + return json.loads(response.data.decode("utf-8")) + raise exceptions.TransportError("exhausted signBlob endpoint retries") @property def key_id(self): diff --git a/google/auth/impersonated_credentials.py b/google/auth/impersonated_credentials.py index c42a93643..afac4120b 100644 --- a/google/auth/impersonated_credentials.py +++ b/google/auth/impersonated_credentials.py @@ -31,6 +31,7 @@ import http.client as http_client import json +from google.auth import _exponential_backoff from google.auth import _helpers from google.auth import credentials from google.auth import exceptions @@ -288,18 +289,22 @@ def sign_bytes(self, message): authed_session = AuthorizedSession(self._source_credentials) try: - response = authed_session.post( - url=iam_sign_endpoint, headers=headers, json=body - ) + retries = _exponential_backoff.ExponentialBackoff() + for _ in retries: + response = authed_session.post( + url=iam_sign_endpoint, headers=headers, json=body + ) + if response.status_code in iam.IAM_RETRY_CODES: + continue + if response.status_code != http_client.OK: + raise exceptions.TransportError( + "Error calling sign_bytes: {}".format(response.json()) + ) + + return base64.b64decode(response.json()["signedBlob"]) finally: authed_session.close() - - if response.status_code != http_client.OK: - raise exceptions.TransportError( - "Error calling sign_bytes: {}".format(response.json()) - ) - - return base64.b64decode(response.json()["signedBlob"]) + raise exceptions.TransportError("exhausted signBlob endpoint retries") @property def signer_email(self): diff --git a/system_tests/secrets.tar.enc b/system_tests/secrets.tar.enc index ebddbfe057d8bd05308ce2e6c3eb38013851901e..4a6a25883c48ae15a2b07b3dc239fc812835aa75 100644 GIT binary patch literal 10324 zcmV-aD67{BB>?tKRTI%=j(HwGYkVgQDUL^p`T_jE17}|rJgN-&;guJ_SWptGPyh@) z@mE8ok`HDq>;n@nmC}LpaPMHm51j)i)Z_XpY04>$w^AnrHAY`-+nCVW!b!8KZ1 z6!AA@USa8h;HqFfll8hEDZxmO_f=Ii_DKer@`i2R@cWKpLHlYS$HkgzXf0TP-kg&s zw>GZu-=2$PCWRb|_6>zS>hCr|FB7UpRBJ5dk8Ao`TFdWwN^%KV7qcBEWUpc@l727= z)Nzx}$MOO(DqSU{bdMBKpa+cqB_tl1v3rAVgdmKY0)XsM%XLPpwUY zHh6JiWdTV!L*At0;K184rZy&lZgbqEg#U#02>-?c?krGJVC?OM$Nh{9%JWTv%9b*8 zmj-riE8sZ6vtP(Uni}PBSD5ps=GMhy z*rcU1qDs%#9AgAgndpfoId9MA*EY1Upm(+=l12!x2=m znn>&Q&Z}t2PnFcQ4gTgS*CSt4TFQ*Plu#dc&HFA@)w} zIaNWihlPE zMXIgnqE969yh}#uUni!-|HpsEbVK;}wTgOzU6>rphL92T&;0)_)AOj^ryHa~6Wa25 zutx4ufn&mbscwA#<`3Sy#0c*1lEK1_Av`uV|E{nv_@Yrr3apLjQ5KqJMOxt}1y;lp z2@B1$ld4wLZWMJ~GKypaMkNEB8YH6~e7Kqzj|IdESi?JQl{w~|H?VB<1W22()2*R2 zBo%d`uWQIj19#3-C}YXlT|+th^#mIBnfi^2nC`{A8CxLF1z3y8_4>am_iRuO{@TE+ z6&TBO=k|8mNNIB>pqPFXk%~t>e_|cX1#o{2*pB8p&p7s3Gc5Wu!S3cX1TC8Q6tS+K z{8`UYD7xvrDorjS+^5yGo8kbFF&7V_{jh9QB2LpknA0)-lEaN*R3g0>n%ZL0J56BA& zT@bmMw@h2WG3I{Z1bC@Bv$at>6&^65JgZDNHOh~jy;_)Qg`diTL4m%sGqLJq6QfeU zTK0Rew~iANPj<% zja%zH+mEJ%f35&MkJ3=w$LMy$UUN*b5J)Z+$ua${g&^*FxElt$h@t@{1p4Z^tQ_*c zMXL?ng%`=p-P!6(Kcq!M1`25pw~W#8wK-;cDuv%(J9HoHR1C)9FkhXX20NB8$^-4` z#DApo+&-?(x@dI$5{qnw@D|8%TpnqO;@QMK2#oxOsrawby@zV)*AEl=*`KFCo9UUY zvxBg>W(^N(gCW6xvL`d?imH5-bd#mof!vJf_+*6IJ?h%)p>V*s^Z@oASNAxN1vGUY zlxye`T4d*MvOErRX@uM?gSt2JZh!EyuipaiU=XiSS`ZbDYV9$bPJZj)0;DLbF6OOR zhGY{P6ne>t>LFyTzMJ#R)Ia{X*IWs+93?rj1`%o8s->Q_>5!g?Y5f+q*seTy4-a;s zCEQ#|estzV#*_clFMTa;t7`mi$F=AE)$j8X)@mYy{eVG6xB&Q`Xux|3sBxn0hdDnv zHt=Gh%iZgCVZ&&Viy+J|R-2ECk^J_oX~E9|GG~e3hCi%{^+X4d4ER4^njgxJKXu%U%k~9zV0e+@<|jcM0IHpz`k$GK4uAT#wQh za513y5IBBVb1rvGH6y7a;ZAa1ti6D|pov`&n_&Uw=eu?TeaeWZ?>8mp#o0_Bh`v`=j zcyYwxTeb8gr3?v7nu;sGaXSOgPT3;;#8FXW)42Pj6OwInyQ85!J6SchXGLOAudIR; z_@;X%RZPk_Q^l`Z(GI1}to#VnRWdQQ|5Df71Jy4~39l{dF@<)AE68eU!^YP=7^Xf@ zq`8jU^cZ~@6_2BcU&91B_$zAi((ohfWEm}cvM9f88j}(q(B28Nw547}+TynMku=#O zp5t7{n87LD%b2CuC}vvx1H>IpbkH$~>f*L=BFKCB07H&Q>&?UTun*+A^619k*C<(EpH12MP^0XuWDjU3OWw@rW0>C@RxYjrYt!)kn!nG5UM~~bpN0vXBo@oFMGxLQAwG=@pKkrq^qWomTQ&KI z5W4?gbNQ+mk+%#KJQid0jF)Afc&^!`Tr=U*uuipjbK-v6o5$~%&pH*1U^+0uZY81- z51EOKY7tw>Kar;+Nm{ZSif`cL&Lo*nhmhEx{?~W0xvI#Y7wf{B*;?TiX#Q`sc5knE zIjLjE-hFgL39L3WPwpTW#>%FE_K;GxLK1q}zr5EqFNMc@ElDo;iTt$MD1I!%1tr+Y z*?&XHJNb*6YoJ$FW2dyo|H|FPiPfz#L;-*R)u=)b2SpuoJX_K#v*~H^l$}2E)TDgv zdxcxh#mDBSpC$cT+6qfJFJT2Adi|A>ta-w&O9ccM=HEPgKqu{25dC; z;bDGu%hXRxZP<*5@0-wJ`Pjbul7S84L4YC%{hm=)LGg?KD?u6AQmE6EvM0tnhgEk~ zz}_pQiMh6zzFOPV$Nz#4YM!5Ng4;Q+S?o5xoMxF0IoBetz1X3egi&51Z3E-@C_c9R zVvu|TaKg-Bt@Ixct_MTPUnf&-lj^<%Zq?+wP9B|wbk^z{)20`x9J(tVr40K= zCI2`7$^mNhWACX@dJo}pzScRbKuhfrUNR@uwbNrAgqB-dS1o{t<=Z}t1?=8Nvh-?&}XH(1|7UH3L)1okY7 z4$9G9h%a4LPY5YFlRO!WXv?PqB#&;J8Dwo@opI%!Y;~pwtE02wef^U&6vpi7P8Go# z$G5(m*|b4S+~hfOJ>m14d0hTyN_972i)Du^^~7Jq_-;R`SV3wSxLxQaRpJ&({Suf( zx0dE3^|P)!q7q~$Pux_wz=TFQvo{HjE@xQD#_jpIq~6hKk_u11SLo}jY!`*7WwdSq zFrr|^{+tYd?y4Dyghhgi+u4L8T=kVqQA#lm%ETAYp{s_ILAP|L3)abjN{G)pqAJQAiyEb?1U~26Hpp%Dx7fWSh-JdSzdN%~Oh{el@%`Q0YVB%{dm-UYOAxIDHOs ztPg6O>#e4Q>FdklE}R9q8_@0pqcYkDeYz|f+U}-@)@ptz<3CcD6Byc$ts=^2acu8^ zSBQ734gMcK?S=6lUeXC9nw=*Cszc;A$tK6!^v^sfALJxd!~U;R@C;b1+#w|XFa(e$p!5Z4esye0rDi)A}53^i4NKjt(tHS=f3<#MRK zRc(7APtL2o3i&^Kw{m#~XOtElht4?6?eCq1vh}OsUK!S8(@${UXWd6w_QLxpgS){F z@&O;4VGl+Ww$@kGIQ5v$$_0Tb%WueUH$~`EwZ;#=m1@38bB_EXOg~DQdr4 zzuz;QKu}Fazw$y4qz_lI+`qu!fRy{AX=^oFwMMwuS;O#5TMSenDrRe3!wUjx*##>I z5+IWr)}EjZBZ-H@AHz$!bfps^Y;R;!Ve>!j3N^QSyEUAL4k}tODzmI(k8v*QP0_CqH;;KQQvFU{8rUpsY(lP8>T5z|j&z&kJ-QL3a3A;JI%}%aBVr86p>@CvIc#MT0+x z4lsXug-lI``qJsm@1OJYE&+z5=q)>U^>PL*?7}JhPaG0Yp7GzRc(W< zh&h&vfkl0T#M+)`4{#tVIyaW3m8j?!Xub!r83qS-lM_md@Q9!exAWskx*KB5W#Qur zC@`4Yg)Vk|oxc{cF3P_~o)*xI1jftevntzB3Me94UawbiHb)J45due5D+DX(IdhKT zltB|Ra1r9kU0z5mwgl4i*d7#K94zQkpM*!ul1*m@$KAE4cGe75A2$J1c_|X7bXH^!k6Y?mA zYjE;Z;Qxpd6@8v}!_1%JDx)4-p8(#_{?A%rOz;i8Sk+*~9}M?V#ZP=u1pxy11}Pa4gvBGhUBL<5E0}xP z$L=Ph3b@5g%Vc<&Q2l^3U7V#Z-SJ_81Vu+D<5j6VfH&qeTt{`iizH-Y-x+zK;dnO# z#5du37=4Z`s$x0Dku$Z|?Tb_LIL{MZ-O5CuKbJG&laBXHbw)9Q+so4R^bTnK-BI`$ z>HxAbGDIJo8)msUn6>ACPi8}R&64xXVfX;)<6)3^rceL29RYsY`JO2<9R_%UJaH2M z(ZJQdbZS5BRY_=Jx#-qvu35kI$IkuTDfX4*hi~><-ipq%O5}%Ji3Go^Hk?G%?NO_X zYfNT6Rw{17KB$m!mY_pOEe19+iq_K-l{#7Gs- zuV;!f3iNjYqI;_pqx_z(g0hdOyU<1HKrV|QuhP{9dqkY|;(T}`JSAiqS zPu&KD#%NEuzXe%pG6&6*`@?Iqv`M||wluM1Fx%)!%Cg+aVcV@Mp&0facIMsDz-^#1 zq2S&`A{|Wp*Of9_Kab!~%$d4G8h!;EZq=o*7c={o`Y=!BgVEsmJgymhewR##U~r-J z88`FRi_dXWI)c8#_Z7c6AP@CNUaU^|W9+SU0ue=AA~100;5FJ2M= zF$M;&(1w*VLN1}-^77P7$W+aZNt1E zic4Uk$Us_2Mg_s1G_yN|n-p?O?+{2EaNKa5lSACkvf+S42y=l^n2)G{gkl8@8p6mJ zW9Bp3cQQL-#&7xLV--E;EBcb6*zD549cEQ*^WE?~a_v#ga|{pV-Ts6x3cbRfM(4G* zQ|6gcQ;6+t7&Y&{c(1h?qh(cR>mC<~lN=V?m#{M)j{x=`#|uuSE6BR;h4K!i=zwaT zExNZSbc!@fm!}Nx51olSJd24T9zo+08dhc~eXbI6M32YsnfzJIU+iv)yBC=*K_d>Q zGr_xl?mq|X|B~bdC~?<@OR)X`EPO|-TBCjZtUU-TC;`nNId=tHJe=N@kdXg(8yOW~ z#*kOAP+xmHVH&-q!;@0KUDY93WIPO^3dva_J)YK;cG!7LuT_9FkHm$&w{tK*QkcpD z_iGMtjlfuHX@lKk*+__3wy2`xW@K^?)hL(A|E;$C#6z)p{4nk;`Xbo+`Oq~K!}3I0 zN~lXywGp~ak!gC+z?X>*Ua--X#9-`jOeu}$FqcX#g+@Al!9DOuh0k4nX-TRUE(t{B zqocP<&l45D|7*=$Yi78YBc@7gn zx*n;Suq*isc5b-IL-b`f@@m`G$_GToI6dN@B3!th9^lq{Do-ArkeaTC`s;h*;jlrm zdT*~A%x1h}7W~?g`s|QtWK6!5HB9EaO*s%Ce}`|uX;@msVu8?L<*akHr}ZyPFbn#G zQbYtRgn4q>2dPDa(#Q8%I=={(fMYv*d$8t(b?zI=vV%Ae{{-S@;I5O0n zc-m675j0hlHSo-y&IL*ih<44`94Ze5>vVqHakLT|h_}O5)?KTMYn=bv{PW6<2LLxX3U(u}SgF`_}+cZUH9V>$F&)J8R zs5K|5s|BBSFY`_qqfX>^3G3*iV61|8%$mdEY0a8II9u{MQc!+W{f}7ho8IX!@IPqs zUxQb5#W@pas`MJUVJvC@jysPik~?DwMb}^WMY{&9BvW07uR!;2|D>r(Dgaa8|Q_&%UOx23}*Z6*8b&R|3<~>4DigLHyA0f0xQyc$IFF_-sL-~FraY(eo{yIGUOc1xGzz&H zTY#(OXhI_>oX2DRjM9`bf!T|Jjf^b54aYr~UM5Kv#YP!59vQ{p zSazJPRFYcLKFxe>0YN^4E{Y7!5`lg_+?O1n0s~x#(!JzN9kF0;Z9dmBa@#%vnLs|s??G*; zVxFwN96R1hfTw$;BlmvJxld&}e92UAW{(UL(wwcj8^gaG!ijbsM!H*tjjJNhrzR*} zj^D9w1t%MH%ENGCrq`gyQG_6_>I6y2(ZmN{5aopqGcC0Utr7K&os6Vk9jYVXt*;35 z{{j^7PIKS&-+UPhZ(z_m679476*k+k8XWzqdmtQPuLG%a{a_49DU)ONShwq0@d73x+ejYOo~D#Y>aCUmSu5Gl@A z`U3`OuhfjzVw8pM0<`KEgStcl;(ta`!V8!$ze>40q`MrqbI*R(K30~bzE$>X9R&SE z04@`OkYCmHjoZsdLSbOwElEEVwNp(&b+TLImAM(FD!;1svtY*f`!Y_u2r=Ts**;?O zeq60gKG1w9VsA?k%bGs@_~jUjphuNBzbieh8)GwCvyIeH9hVf>uU|*pIeYH-z;9IW zA}QwxTf!UJiXd(l3G4uAq66wmh5sFH1l1sTrT0qANR({^f&_j#7RuBY%Fg)LiY)<# z-6<_N3PY{>cKKo}wz_vH70DUp!YCl5YdJ!oBya2CeeD|BqspPlw~qWNTpM_{*_Zq8 zdjW;;MogAP8pM}7W58vAWOMzJ3 znp!E+l3{YSLdl27Nj5Uh@ch5>BAFN!QLuTLnI)+a|DXikX^Q)F!1*W^D)h<~z0U-Z zOx7bbByY%np_xS83UDk;{`NaAexSlCc2wIF zWWf}sSE+>4iuz8Qx-;&!g;u)LhMmuFIXfJzByU_)N&>^VHUI~TuPmfala1!l_i@m~ z>^ugkL#RVx!cF+8mNBkjjr>ZP#%_)AMej|t+|(4}qRe6xJyMv$2ei}^wyy)Hg+F5y zga5C>eb!9^R`>SBqyzUpY<$o;DAGh0FmK*lMm-$?co|Ru+w8qzbCpubM{bU>uq%A> zA?Jyl`cXwG_6hkdaK@Ao?b`8q5J8p4Q%3*P$fTP|z*qAXZSF(-=m^X4cf*!>b8>1g zL25KCE#H6}1@f$>qu4V6&8DGmHg_XML!;*b5M_p)Ev7lBYM)zEhiMPXT&Io|a$#}> zlDbh1!7yZ1X}&Kt<&+ImfURZRkpRjI4ykXZaWJ?+P-2&tBvt;; zX$1q={SZuNM+)lf1je;_$~*n^mHP8%zGMm=fK1mr)C17-fCQ~g&kd6z^Y#L3L5a|D zcgOiY3`#!nc-ua=WXNiXl#$Io->!{eZg1c3JZ*SuOEniJas<$TkNCHu!0JhlT>V+E zjvS?#a83DbzX;A&#jK|UTwID~En`qRY0bi~10;&n_JB%r4fZZ7?Ow_k$JRrHR<&PK z5X|mb+FWHToli`ZQWg?*HN#f$K6@LC(#4^8aoaUic;wlR5H=aHmdw6?s!CCAdC2 z$GoV3=}M7A0JD=hw?56hsjNo?;0RIW>*dhKo$;Zs1MEXUJlaC`t3-R?{_w@ccu*`b zw;c9>i@PLqUPgT<4B`4*0XVvzT>&Nn%NJAwTuPFL4E&Ih%-$T=>>&U6>|bEeN`s%? zKWkTvY%X;wtDK|8Me!`T0^7D7cUBPwq8tob^wp^KUPm^21hm;6YKw#6;PWy#hyl#t zbQeo&2Ns%bn-dB=ZpB4s{hk8JSnirJGYVq=5{B2~NQY+1)gpptc~My+)22BX|4e?n z)900_S#usA%(w8W*o6P7yQ~JtC#7iyn|GU70YB5nE^Bn(qBUp*@nMhnhH%45&O)MhC8c`Id);oL~)HJPx8rYE95u&8ZCdjzcnPjBY83Go1r6beqCgY}+Hs9{JAxQsM`c=OudrNkKa#Ff8 zpkWOV$;pKgwCJJNXZ-Vplms8w{{dv6t7T(%ywo^-=+RCqcmiL=^A^XW&MsiMdhpJ zYBnYd7ZiAP59odhzKe z>US~AWo`Vu#&j~?C6znDa30y2v`@AS*9tT80E3HzA!N9g3S>H{Nd)8W;D1lt4`;-A zb)#0O=#u-1gMv|vFTfv5kap(W+kLk^O&d}l&E3`ZT@%jlVy_V9DNRZ9L~T5Dt)|tI zu`$oYwB2^6@-iiy$c9O+{OkgJ;DA9Z`m#dLqk2t9GJyj+Tne8|@K&HGGt3rcFX9<~ zoG8yRnNkm-4qL_7=pR&t8A53}YdsDIBSzK=1g}Haemf()pYWdT=Z!oapsHE{-PyeS zbTFqSagw?ba_$s9XH$0x;g7?x(IHAoIb6G2?2lFJC_G}@+Fmc{5+=9@>aVj45oGNB zd`LF*&Yb$YgR26)(F0L)X1!v;184hhl8r4v_1Z>PacaY?V$FlelAD&080v`=BAbjq zYVUL$R%Mj8S{0SNkeeAf2a~OB!3!-ot}AK^*jBd9Vr50M2b?x8OCYF1z@I!%R7bss zr>(4OvQB>G$t(sRq44KY%o=4cvUvW=)mLn($i(wFtUq7~luid~ua5qY0~_H1byTg? z$4L9WXhz{dCLc_A?uuv*%}g9l^c@Ff%YxxGc>biZLhDS1?_I~)8oC7Z^RraFIAcuI zh7}hC273}OH=}FK(7C&H-SORrI!mbDmarDb1u{PNt_PDTXF#%;d^~H@%Z%MKXdetU zqMs}vQ-0dL43-#Ou;xzLDJfQ8ZTi*L(gKP54}f=4IxA+!_5 zs=DSdGLC|t3veT}vnY5A!l4bP&Hug{cO>~|x?77(+F+~F&*ZhJy3Llaun>nRWjz3y zV)bLj`j>1~{32jvs=LJ8$8$)+mJ22sXsNX0~l1xZY#?#$=3O(+gGP70_lz<;L>YINZrAez)kUVjzn4vN1c zBWl$)39h9R5IYBRA#k+ga2?(NT>V1nCYDQTkaL{yvhcC(O~@*f;+I2B@6j}5!+nKw z$aXcrmO6xoaQwKjrIN}gYZ#1s|6p2LLw0(Y)o-stwPO5lb{H-9rAZ>Ea0U4&y?D55 zy4hKzrNHzwINqe0rNw-kjfh1PjglE6T-0Zt^YpSoe_6VHH5++Wx1Nrp1^W(oE+OLi z<0(g3gmKh-extgV*e}eUzx}Sjd0-xHl5DS~sbZb1yTvvis@riZT)Bd6Sx>jH)kL+9 zVw_xFZN#wR0PYuVbK<^KTP-J|11G~jA>{IZgJ_1*bB%zd9s1!(RLOLA2afku2*avA zcaz%^5z=Ls+(iP6U%Pa?$=>Z^*p^b@nPxCcC8qeI3?N=zYm0HhnPH4kx0gv0|EmMa zkr+JXUfz$g{j*2~8@ER55xaEYlJMsz&JC7g=}%N#lcUdWMI3kzGy+sBN`9YlDb^9~ ztjG@Ulz;qr5Km3n@f^%kbXXM^ZSk1IZn)q13$I)Jd#p@px}#1)mL<4&H!hMAvrv!Yl3{nBXM&Aa72=w5^B_Sg9&iArhbXrh#gsuFeN%(w4fhgO(At#;4d@9en zfMf-D*}^h94|!bk1kXetra57AO2{GAY3Az5_tk`u$HLP9rG* literal 10324 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