From fa08625f42bc27d5e234b9ed9dccb2a5e9253c42 Mon Sep 17 00:00:00 2001 From: David VIEJO Date: Wed, 21 Aug 2024 00:51:13 +0200 Subject: [PATCH] update --- README.md | 4 +- config/manager/kustomization.yaml | 2 +- .../mainchannel/mainchannel_controller.go | 3 +- scripts/.gitignore | 175 +++++++ scripts/README.md | 15 + scripts/bun.lockb | Bin 0 -> 55877 bytes scripts/index.ts | 1 + scripts/migrate-channel-raft-bft.tsx | 451 ++++++++++++++++++ scripts/package.json | 15 + scripts/tsconfig.json | 27 ++ 10 files changed, 689 insertions(+), 4 deletions(-) create mode 100644 scripts/.gitignore create mode 100644 scripts/README.md create mode 100755 scripts/bun.lockb create mode 100644 scripts/index.ts create mode 100644 scripts/migrate-channel-raft-bft.tsx create mode 100644 scripts/package.json create mode 100644 scripts/tsconfig.json diff --git a/README.md b/README.md index 40c4d9eb..c5b12bb9 100644 --- a/README.md +++ b/README.md @@ -567,9 +567,9 @@ kubectl apply -f - <z4HYJKsN|cl#L`Y?bOqEK8h!jPX zQc-C{C6&_fJ!{)<=RN0lPU-xA-*^4L>uO!+K5MVxzSpy!^{n;2``vp>TvQ>58ld1x z@l_!E28*}``OW~ydHa#P+$r8B?VCY1E~Z_s1P6!A|F7` z4!HpXfxrp5I^;7Umxr7U^36nVZwfV#;OR#tBaIsX4ybRW=iJi>gxOGLgdFkrK|SIR zLU|VCA7L6uXA2X7FavTQiVs=N&6VH{Wz=7nc~Xx^-Rw*9q6Ajab&o@i?8>0$Ayjw) zX(s^b1-O$`m#AtG0?3>63PhK#p|#AxCynsUH4bhzBHO&sNA0pG+djc~V_{ z1L*bWHPYWm4)iBdsf1_XN;D5QBGm(dNcB}h?VeQs0E!QR5KImT@CzUiE<-0YFK!S= z`Zu7AcwI0)vcH+0J3;~VBYFlS8NypA&xP^>$ax{Z4mq+rD1ZWX5eWW4zMDyYZsfCc zehK7AKLv86e}rE4r+U-$_COiwWB!hYGMXoaN(~Gk6MaZTD%qP%rIJGf$-Zs`LQoI| z{!btfL#V1K-$eJXCsl4U(Z`!W4A|&T44{%-H+xfjVdVp<-V|5p7f6IjL&D<1$IFcp z;7=gz33pvu; z3QkA;tszJLk`|iGg`tf6TLWdZFG?Xt<2{6Wl;_Qx@h6~k3(Cl^ zhapG)A$br3+{l51OVA(LSq?eUg*IBd^`goCh>QB4A>C8t6=30)mWoX(@AZt7T$806)Gq& zB&WKx5yu*v^51-EKI&@~B9pClqx36p=vE<#oc_%A60&lhT*`A%0r-YNXnSCXnXTN(}&ODKxDpbp^Gd#An zVUcEqMzD?3g&eh{lFE)*TXX%NWh%`Yr_N$5za8LMP}XY6Dpq%eYYzLd@s|VRf(9Si z#j5!9a$YapEO@%VJ*zo%aOO`R#2)6?73jnSR1qniEFO2G!q_bAf(qTZ3|%{l z=4PEL7&2JG-r-uJ`jA@496V00-g)GVkzZWF>xqzTjaWb1-aD2ak9ee`ckIkaOMc>) ztzuI{xp_$`;+@q24!u4z{TSVwLT1-nOnT#_zAkf&V-sviV!IbxR#Xk)STqqqv~1Hj7ar5ysIsi#Fpd;jaZmomlSSWemrhS zMMBn2AT-+DC+qFC#NK$$k9oz6jGQCgwhi~2Clbdjomn(xjI-|*<_eq}KdzbX?rd-) z8Hw zxsojB)^F%PzWJ-4N{*2J;llkd`T06yZMude!u+`G=Zg)9*RhG6bzsQ6Yy2VG|BE+6 znZMZbJH>uVmEI$y!$WQkHutpamRl+`OZoS3+wj8q_Gtn&V5!t;Ngc7lXjh?F>c1YYDQY*BH6fZPU_&zWG1NO71 zI|Hxg+vFFfw>gm9cO<ZBSTheTDdru-btOg z=eW7f<@(ezKZ!+~wW}q>q;@ejmef4(V7T0|V_BJ-icw=* zhBjV{<9R8TEg893HkRxPw?Y4-#7JjrLv9Y|94f-4Z^{Yd4*|mf_;9;KU;&_fDhcCT z!$gefe8hv>vneHvp9=hCz(-}QZYly;-)`V*0w3A_D+%Ls!iy!qhug+qN*Lb=Uce0; zjStsXbgi0F!uY}PVma_JzhU{$L@<60ywC+cDk}ou+B~I%@g-nD6T1C~_^a#h1$;f= zBQ6^ISNTQ2N9%{~R}dfWMW&Q6``-XxANXioxTX5#@s(lW(fCLYX+StlDPiNs0$+#D zM_lnKvHytm2H>j$A8}Qu$VJ_-@jn6|?LW+JczZFWgz;Cxq?hCPQzAnrjGqL24d5dl zm@?&r@mqn9{D*OWb^i#$#y0>ynm6WysThHc??~sPwf~ib@w0$0M;{;VL#LcD{zKry zy3_1OHvH=T<%joU==?%+M|{{0Q%czQ7QjdGhst&Uto~;rSU)asye054|4nrbFun`$ zjp^f0m5Xe{`1!!c;uov?neBMKE(Dwk-GBdP{gQ#NN9W^pe`W(_YZvgffRFm4vk&tl zD*q^9d=7Y%gY7@$Kdk;oT-1j3GX}mM&i`2bRQWrA57(VY)E|vM)iuP%sRllZUyKjo zIHiQ~hk%dnAEr7kCXVrC;7#vRIv@FesyY~-1bl4%s1COCloG~I20q&VXj~=$R{t{* ztY07S)#!Y1#gr4qpAQEP+(XjFpX%9x2Eq9Dz}KVm@%8BSj}pdD2R>Rq)ECWvstM!Y z0=@xm{9ok@!owTHNBa(~!LRN=GVqcAFnfM={3F0u06vNvn94854?6^{6Mg)t#xAlE zvzG$=RlrAe$p2H-!1!l@kIrutJBT~g7{d79fRExIjs0)NSAm;ubp9bd%m#S*(}a!h z1$+bGBLZ53U!8vm@bz$fq%)O-jXw;06#pphP~ESNFUvJKKH^R_c9DJ9_+G%5hw;(4 zQ@!`X^V5Nk;vd!h8-6$NvHNede!m(&obV73@zMBGy>_6!*!)d_|3A5YMgbpPKaeKc zzf(=v_%*;s@sG_LtN)n})^8m6igZ5H&q%m-jK2tOdX#{V)*p_dUyeU};4cO~+B+!z zuzA4CA0}-4WZ+}(zo#1eNEqX{0w3G|sD7#%7@q+imLvcF%HF|`uLgW&;3GZcgI}Hh zM&P6Uhhi74->>qI10VVC-`f9%K7VZ7U-h3P@1+0#E#C+D=>7$>f2#K|*!txHAH^@? zA^%}I@bZTVbx;gjPD8j6~IUH$9#bG`B9GXTY!(we`Ncw*nsg__$T?; z**8@iFuo7)vG_&yV)Z|=9p5keFZ`eJuy$;m8^A~R4_Fy%|5=Xpmw*q|4Cwy<83*5v z@!f%s&JW}_V=4(7PhdW&sf>;1pQQ8uZT!6ez8;K^ z;t$!6*@edVQNsFZ2v43Ls4rIcBOca<^$iC;y8fc`57lAySigVDv3@mlKAJzOo9Y^1 z{Bhu`!ubC-{ujgdAJF-W#m%pd9{_wk;3NMd8>YICF#FeuO!^O%kquKl$1wgq;3NA{ z`QNO+xag$)*#5*o!8bp1u0;a2RI&#z;^N7o-@JMtl>3on0|uzo|pNAZXF zzmhP%jM(J-F+O&V|4a+xdjcQLAG`kl>iu^g@X`FS`2X3sSUWa8zxX5{tsgcAtj~{f zjK2!_dN4jJV|(~VJgg1l?*qO*@KNmlO2YUzfRFAU;Wn1=D|5j3Y!Z{>Bmco$v0vVQ zX23Us@qtQ9$cCvTZ2Y~z$H(8V?%!*`*Msqq9>TAV&n`(I7}EJ@{gEH1k}&&M0Ux{n zM!a8*-@U*`*FVgDY#;p025kH``uM2)Z|t8T^~dM;|Aub|d}FX5#V_JdHDUH22fhOE zF`g*);wL%A?*~41{virh{}cN6esdR2e*TO4qu7DB6H`jqI3(aNq1!*z*hK?i{B+>! z(I(I21Rp!H(`OeJA_2jHXg8*vffap05^#y<3ae+63XFG5{hM7!slpQ9RNBTi~&f;1b`CCkuObA0TMn( z{mf{!lQ}Az15j=OK=WP&K>BL{DE%l$d7Ix8rkE??|slIns5e zm+?8WYXiOh-{fc|H*BRy}T*SFB~HhSI;InwC> zpoDT%zK#lzP>ysu0cd`=0I2;o0M*|CUe5=q}+UYDJm=or;E;3EHOR) zDx=lgyijqocEU`4J5s-P|9#&@!S`g!4m@Zvw{LmwwtfH1x)sMY`D`R!boNM^Ji5=* zZ*o>+(WB^`^@F6m2RL1HjldF2Fzxh( zhzzAJn`2`F7GFAsM5W$2uL$&$oK>wZJg2arUN>_{S{J8_&V4K~MJ?o3ju0|Wy*wUY z))KYh@B%9H9S!#NHFM|Xq?2?Oh?|-*RmLuq?*8hR*n2K#JI5|vpOUc77`B;w3zvxH zCPd?O(KQE4Oj3zi3oRMb7mc)svzf|0w;n7Ac{3ITo%k9Ho(V z^K-vfT<14edATNu5rGhZb`vi}NHoTIZT> z+45wKQQWk7Cq~?QUL=aG+uova_(cil=QlNb%}!_rl5o0gcwM0_bH?v@PrLoJ!AGo_ zi&F-1VFOdpj&KNv+zW_4G)f!Hh!dP06_nY$JJXJyeF9w65w7ybBI2Qa^r5 z^vSA&me(W{F2tnSZ)SE@FZyBNqB)=Yi?Dp0E_{9Kdt%D)S$U6Cz-7d}-7;#- zwS9Jsuie1sI};w7iF8=$x%Bn z|D<@dxPkU)nSW4+P>Mp9=(S_1@i<*BOcZjavUeJ1UGBL$4%=n=H*z;#pU90}<2t)? zl%aB|skb5P0(a~9_U$!XS}a_85BDyti}SXs;#MPTZm(J;cWh1QoLZbN{(UzS-?UIA zff31p2g7xaObtdZJo`CHtM?IPS?rToJ}B^7>QA@smEN+Nmnk~7m-;~^t#MBH0ricw zB#tfL&ThKH6pYj5!P~o0B6{f7m+sv98}rQjj}$HPUzd7ne#NS~G!v&Iwq-5>jl&`X z`M2fHY+vHRCNg~Tx_JWW(pdVfnoye?#}qfKE8%o!<8_xlyP&aE&UpTu6DymW`6k|V z>1Hr*xKM3M5qB`Q$|x5Uzaj4w9wI_KAsag9H_Jk0B@8t7pcACyq|!TIvGPT{_2 zD+^P~P{qSXV zT^@NWiqo3H26LRQ0N&m=wh!#zwE3KufAAdO%iaABA8hY2>~!7`T*uDOSCY)_^RBkX#jZZcP$te$;-*p( z&R+N!`+H(iR!O;ZP4nqsaUf=y-T_l=G&y04GZo%>xC>{V_X);!((E>%{G!2gl8R^Z58PQ4VT z6%n~36K9@G@Lo$z=m}9oJ3_NSqUKQ3)U0{b?Pk?;3TZa^{SkI9(yUuAB6R zXDn$KPLyuY*Y3U7(t7WeshGIoi}~A^BwrIsyx%^Yv(QNT>N7u&%BzdENLWcYL~Ob8 znm^WPQ|*y5>l5C~ak|)ZC?wA`q&GcP?6UabgIqh!o}Me4f8&a14vTM4M?AZ-eRYw3 z@@|hpzg2PkooY#I&m5|zE_2?zx}o`)2=}+0N_(W9Tjb$%=i}{VWqDefPwkiD+E>k6 zCBCj{+RX(XGv0i8Xg_3=(t0&icSoWl*NrX8-HoZANFl*}cDZf=cUR?VscEmCr>Vu( z$Bok!#_Qh6+AVpY^8{zhgMnL*h|(LLRHue~<~jFS$+fu6Rp;zi#cAhb#f)x5yq{mO zK(noQ{QMT1(UJa7$%CaGc(P=1^l1FWB%TjAU6Jpq;9u#W zCcf)Je8Cz~11lQ#B*;GfxY2f^V)`wDK-*AI!=df`ibvIh-Kt-nd_OHp*x>rx#l`1L zH)peq1WE1k<0_V$gVPno>xM0`{4|eyUQcsBPuCvPkZg91ercABgP-JgY~NPiajc5> z&5Yqk<#tQtkLhevO7Poaab$OfgM7ekRec%zEq98tak^r7UE(2i*=zebD_yV1G2L9w znXCME#LT52aG!Xmvd*c?(Kff7U)~+;TrqMdKz+lsOB=boH=Vn1HA#5oJii&424@ab z<8;OGy4E&2!BQ`7r!qV8a!$+LUbxKi5!o`0Mg1D{0rZ+w0d(M?Nl<=grSpmcO<*A_+d*m2O0=^b@8;cNbY?QQZt2~U;mBj0+jfSpKG}y!t z&#_i!WYe1kFa7RYq<3;(KklNhmAj>~(?vJcqsQK=^s@b1rDs{=dglx;GjvIq?LD7o zp%mR~?}*ct!s{k*yS~wD&O83~NRKN|+MR1DDaVbEKH$9TSx?dnWU6C`)= zCz9s~``?6}ZE$VPjT}^-+3k5YC7h)35U0BkuN%Lh>C(qBMUDF{haY~EnLfvH8~4`u zmYz2&R#@p!qqQ&Yo^E87+d>V z>AH7n)Xn?S65o;YcDovRPV$%WxvdT(YiGHhyDpbo-+xw*N0{%6ptDf(o)z!U#>tXM z5(HLnQ(~iowGU5?Al?ts-&J8>koWITQ)J?OmdDLyzTbVp!k15y!k&Jr%6;r7DhAH=c7GVJuo_)hr6D-9>*83s z_j{ZlWbwN99V!OndRFf0dERU2b)b!+m}w?(r*z$!&a;`4PK-?ZYSJl2H9Eqq0Zb{x zs_1m(Guh3YulTd0jM%P@MN3UI9-MBst^yFCwx1K2L!jxYvoTiu(r_U zRP_+Lw#rJK`xF7nVsBodZX63twGM?cn6C$Vi2XVTJ zc-_4r-SRWL^0Ll(hNrb8?^wCrQRwB?fo*l`q@nU-U~X4OCI`4v?bmM21rT4m-k zIy&>)O*Zs>@poM|AxN1eCc=Ka#P*sxgKGEyP8Yt*^F1+*lpko5`(Wsl`Qgf{n~GBM zeFC?vjE0pLIM`GX?`S(XyT-3QEWGgWPW{W_AL4u83dk0ZRb0-L=~!PZ8!z?o9t%!a z?Yk=Y;X?eDRXS_md1&6+wm9aTx#kDGbFZeADLg#jxO{EM^odznn`Bnb_8~-wXPy1J zCyCo3rq+m5_3nt}Q)yeHs!!+nak@+Jx_b@cH;lW#R6j)BMZLd0B=4kWEfb^vL`J-f zoO{&^?jvVndUDjF&!6aBTe8)!q3BEQ+{0GyL-v-IJ6fJ=C18j&(m0GXuawfmw z7bdmoN1FK_xxT%}^=43I!nhQJ!dbSm%xn16K~wvs<~L++u0L_K(&Lp?+UA@$ z$sC3ujaKRd(a+C3b@e%TW@5XGr?2bB?anyerFh-LtB2NTNcEVsIZHk;N}hK!x2|S- z=#E?ycCEfUTrKr-Pu!+o<#{ATUD;Wq!aPo#cl7AD6E(uES2g2avDG`i|Af=k#Ouy} z?;T`0UB2VfD2qy@+hgU0{1z4W7$!{RMvV@@2Q3B0NwO=~Tqt%|{q$V=D*Kx3rS7^L zW{7c?Z}Pgmvo>^|Cr(!juNyH$TxD%#K@M*%lUos$vFf~`=bZZ_PERYU5qNRpo~+2hGWE!T@s$I`V@GF6J~b7-S(UZ&!If?oznbxS zQAwPx&UaOa->%kqb5Gpf_FgJ6hPZ9s$#JnaENXJ|s@j~ly|Vrsk?MN;s!v~!Z`I+N z06yatFV**tpSf61nzift7nvC^Dm_o9;dJ4c_FZoEk{V6ZjD75Q<9hhk@L#_>|LE#^KT@1clv8uu z&KkRly+sT8oj6oeAN5oS5%K#%@4G7aVXNlJnb}U9W#x`VMQQ<+yh7PYsf*%>=ew9j zmOZ=m^xnMb2ZmE3Drb`!1{8W2_&j4n9=JQlFJY>Unrz%+BQ%g3FU3%Y?JcXWd?0!-GPhyqMvf^|N@w!EPEj+@~tQ#+jEP9v5xa#hgRGV~h zR@FY9>Ca4OIemFAcENa)rKy%ea3j-!<4aDdI3;X3VtAG#OKbm~n8(F^S8=+_@wyF} z_7z*5VA=g?HayVqtHreR1U;&R%1@Zbh-M_pDlzn4~vBzLH+S z>pYul1z+x*z4e{k<~ethWy5D^4>k$pucW?`k0uW+7%m?8cJl0vGf75v>CyY_F67{! z!4uI|S&E%U9{f0*t4sy=0Yn8p+1 zX$?_V3wpHT7erLQ6wxZvHjh~F3Jb4e^Qxz4<~^0Q29 z`%ULHveO^O9cr>ObL)JwZP@IbvsyB1TF0?hk7u8hSl1d_(7C~DCDo-!&FK2k9g)=g z@sh5*4%aww_F|uNB5kJeyUNyjf*hWjMC-Q-6LL+mr%(6r^hlFZ7N2GodFhmWZ-0at zvA4vnzj>~5AGNzJ%7(IFdvT0^xQqJ+W&S7^oG$vj6iZAs8U*v>vB#7>+_uP3i!s`KX?@Sc%LbgT z1ttnPQ>tOiTgDIlUl&SjyWIQXnP+HZuHljs(nVF9o}^^lF5SmxvrVdOR!Zl3_O(fd z&1-i*9KN~p!+7^4(NHz#thl^-obF1z?vT*u<<1f>=R6y}Px$=VPOgeEqBirRgY(^w zQFkQ_4-RQb))Kixw{zak2p@S}U?ukOa;a=iQ?>$e$XG~a+mJ9$*AlNA$n=Ws$;xk6 zxyroDw&=KD-LmGum+A$LG2x%O6)U_Rec0jkEU)tHw`c9^Z}e!d%JZ5@y0A)8(^Q7# ze!kh^`2qu^V071 z;G?`MZ=Y%R0v29;<+Jb8o8kxJ*2^FAuX?=SHD!B*#j}Mi)0{iccqV#v9xuh|TH|$h zadk)>oMIDDf+>&j#A=N-pHMx*MQsyv==CK*I4@auZsqVl!9(ULHFEcxt3{cYc` zs}9}Se$De%l5bFZ^GlrWYP>E}xw@tE8b|7@)C-aiWKNS#ihV4Wt`94o!_U9pY)^IY zt?d=lCKgScx#Q8u=gbH)pE?^>Mo9F`S-y&zkZefb$t7IBJMg@mv; zPIoO{cVD>I-6a&)Tkgg8JUKL9u74j?GT%+8?#(AQX<6$tn>UxbT$S=)NnE+XmS{0p zQlNP2bNaCYdG^E!*SmMsP26YUbZzjuuU=*G3prhm_;|nHp{TCWB-;0qK*@B)bL449 z(p6bAH_d-uS>${>rcJR_$g=lQ?3}>LBlZ@lr;~NB%=;W>B6Jg{Ym3*-U3ysEENene zyh2ItNTPPB;r*gie??Q)1L7y2bF6=4zkdH!fip#RC+94(-2b#?=kyF;-W|nT2Q>?B z@9L~Ov-~+ucO72W`6PRzOOyF^X}#4EHLedOb>z54vz^!j~g0xLLWiSKG= zuoq_%ulKoW6>ea<)fm;}!cu6jF6TJ$ZXQn84zDY5U4`8Aa>o57Yh6c3Ix69_8pCI{ z+WSUE>@sAY6}@g>!-0f?Cv{S?kK(k>Eqxaf`(sF+ z>G-1x^K}d>-I@EjKS!+FJ8O30dl?_)O$WF$*{L69IRtj!2{PYU<)KF86gHlBZJ+AV zJA~ohuq~&|UuJJyf3w;Pr|W>XS15msyj8n-hRWsI@DVfN^*xJk_b5N3lzw~r`F*|I zmhOlKp1JvrMh_R7H3wB2T+fu3=s9@tb+}Xp<8@|-Ir{kLsg8JE7k}0ZoWd&48J{1W zdAV5LU@>2L_)z?<5XX7leGax38`Q>Js(UQv`_|3lJ=!m=-|KL-Ha#&{!Ew43UkvZ! z5`3IF;dSrF99}g%Tgl~I%ssvIsv+4uhfY`yCyEaG@fy0lFu8v8!uZ0}fl56}1R+}} z@bvsWCl62@nda&;N{WiI#l|khKeuuON!4Km-izCSW;%(;6nW5~@oo-3s;2Hi&% z>jumcJGbuH%)X@_5yBoPS;Lx)U~T3D?K; zcsh>UD3UZO^gQ)>Eu(^7e~TY$giG>A^F_tmsuvl|WLxysD7P@)z_HebbJ#{!R7v<~ z&fA4=tg@Ejbl2l`wOaZT3T}C>OBA|(y{n(w*U@_SmziWeka?v|o{ z#p$}?bvvXBp1!OtljU&~`EtDHaDsCq|G0s4t6A{k+a3qy=W(hpKXZ2^@Z{VOXVy)n zor24x6YCY(yzL*Jv3{n>?UHJM(6+~RPn&1kv^5~7hXD!=!?A9!tIelPH%lr({ zW;?+{9j`^(`EpceAMk6ruv(VsNaZt`-osfk7tgqD!s&Y9b+ePTpQc{%b1M#S5aV^= zD<+soC~#GV^Id+v)9%Z4f3v9hafc5tbDqCQ+)v2h22s#CKD55>Q+p-T0hIy%CmX_X zy54wQ;ed9w`L7b1wS8W&%ij{;nEkXl;?PzS(+fKrW_vudQ+)={XWzV)|GNFe zjonX|o z>_CiB#j0mdv|6<@JOfX#2Rpo#?_TxIE=_%o*VFQ%opVRTUfAJueet>q>!*2S2$-5K znMgQsu{FLTt<}oIlV5-NL!GL+=Q%!R#Tz}$Uy;16Ek8fuw04=HUSYg4%09GLW9PA8 zAI)#wS8%$1cwO)NUq%_$>u)QKXTR}ErrCb(vEqBjjrAo~YYg@$Z|t{Xxy=8l(x=Jy z6su^$;M;MpdJEQ2DZls_`K2o3ie}w4I9-3d?)V-m>AL;W>DyeMa2k8Q;}6eSy{dweZHC6OLVW-S@ZM z6*pe*ewb{>?)C_0ZvbA`Z-Z|Ti>{gcoZb@7pcM0G0=uf$OQuXu_o`a5H}Q_gf$gDe zL0e*3Un$IeSDCLBrc18&idyD=PT@m;%22{6|0SF*6|XC?_15t>5-iEK$3i)094#c( zGx~`y;;Tz1^qRjo7!mt5;`qs|xfG7n`2it|?8XZMsRt(>4b~{%*?QP#-+HU73^?6D zyzZu0Z%gH)Z!X5349!ZEYIJBNT&$Y0^n)VB*7?|Y$@>WJckk52R!s91Sg|}OrGMWL zqcx*sf^v1NmDQFRFIHrQ;&g-Xx{*g8J!X6G-P2}oSxU7sLb^BI z-N~*`l<_WK|H>ur=)Q!E2T!f%e!j-_>hXtH+bP{g#ac}R=M+<3ceHb>nXJ%yk}TeT zVO#25oNfqSxA9Kw{^x14!~@vV6AaY9Wf$(N8Pf=?JI$0@Oxn#nAu~3Uck@M|ysfMF zLU-~!U-{w1(8Dp3>b~i{2WooXEq|Pk(+$PzdQPwz%*&c6RgFA4Dm1}GHNE8_U&LbA zzD@MDzwMV#0&XYRxZj=LxG#&Kl;*a@Ot|b*oYwPL-t4$Q0j)!Ap&~e4?Dqgjp6S(X zt1mWL8_9gso~AkJ&kR4VQroBwuL zsm@QLP_Qgv^9OPKc@~DZcjXNpjr@c4Uu%gQ-oEx&{UV^4I_t5xRQ_zm_=N|F&xRZF zHXMx+3!E0s^1;f&dgC4YJEj?Bmma0%K3)G_x+tO&XYUrgZs`dtf9_M{%`2$VG3Uw` zUQ!Va@|nRo*L-`FdBDs=6*-RHkuMb(O{k2DO{apG}=` zy5V@;+?VMgd+%kql|7~uwiPtDbgkUtQ1WiBd-I3*6(d&yX3oyCyx}~dIPrcYiFmxb zpr1vYuoJf{xsbzX4bt|&CxL+%WTs6ikAD=yA zAaN*PZ0VKvRvh}rxQ2_3WkMUBNyNj~lydLgbxR~ZP5OLguagu`cPl0eIa5lf^yTNX zMmVp^@vl!RpWvyBN%-8bY2nO*73#x-mre`29V(l_vb?qap0NmP(k>>8{9MK2oHuTKxEEa5V`y^ppc6l#OHwv%I z`c@}fvq5{*MDMyz(!dusdp_~Oq$h@RC#o)0r1c51^W`uclmBw&E~liZog%^H$?V<8_ZYNv|934_UY0>eEv7@O?+4=JQ^!Sn*{>%Gs@hryK8G z>guR77CP}@AWO@Gud;cmnV63CF^kie-;Hld5qqxQr+~9J8n1h_w(R}EqKs1uvLgHS zp0wAT2@Y|e-gWUXN5UIDA$G#G9mayL9mUK}9#z#m`vxk)rdt?>S9TV{Uy8}VrBV~G>O$~>7h4WHYZZszdw&1-sH zr`dIe$!vDX;PH0<$^!nJx~iqJID2F9x=C(tmakV4R^`ZcYpoa@t~_&`Wl@;Vt)*;N z7CY_fEw8-&;97@M!bgs0D^hID&Xr|3*;SY7MA^NKNHwu4|8!jgr@I5MJA63d)6iqP z_OH$L%kGyu$KJU3$xS3o=F?)4(PrF0*$z6|A3|^94 z>ii01_mVVZS8c)hcQ;-)fooSv+?A}XJ3)8XXsWL$jPcbrh&tchC%C&ptGI`}ZASPJ z=}h;f3F9J??3eZwoqWAaNOJC2+tWD`_G#X`QiX83@pxTMqIFl0Yw4*K=MvX3Csd{{ z`TFHR%Jh|s6Rb!<%)@U(rxhm(o?d2DadEV*R^;~m)ix{|O$nYHwX$lr{E2+)I&r#t z@VXbN0$Ka!)rOZPsWG|lieqRaZ>e18{4{In{CACPTWp<QRC$|a73k^~<8%}7y6L)N>{kZ5!lDIv-E-KFr15NQPA4lZsQhMjv|Tfu zTRtIJGI~=wqbi$5@f-VhoJ&i6M-Hz@Si&i&xmeJN`?Wt#HxaM9y>5ocU{r>i_K?Vf zid&cR&a+vCytdoEK2vE=_D$tiaSWL^ED0jlw(_auPGoqe^!ESw#BJB?kr+LrZCM?UVU%l2afB&HLB_GSBcGEsb%RE&p`Mhk0 z_Of2rP2Ma%9x6k|F80agK?5Ql_~#}2@VaNxW+bSuyH4Tf3Z@z=c}p3ccsim^mcKl+ zO;Fpuy&>@U&6(_}o)_}mn>Ndx4whx-DCmz|q0DcvU3mTLQ7LaloV`hS-Hm$_3)b<3 zycaC0_AYrnc3pW_=wf-bypx}8>(Z2Gh* zvLSqC&tX6O^SNTr$`-W(OG3W3JlJQI{%>%ENzJF~*S^d20MhEM?+5MF!J6^7T zbKnYh=jP_iimR_Non{l6h&a4HKxB``ZJ*J@DHD z|G#(u-3!6lM?m?%O}}r~|E=eK^L~5aw+DWE;I{{Ud*HVRetY1z2Y!3tw+DWE;I{{U zd*HVRetY1z2Y!3tw+DWE;I{{Ud*HVRetY1z2Y!3te}xCQu ziQeAw-hL!6cZxSzer*7myi7z{QG`kfBm2287Eu);dQ&#~`njXe9I^EE+hjsz^!-}& zc_MrdgO;!|`kqE7y$*d>7WJ71fUkl z|En5+`lIiLqPpn-?Dtpbd!49`6@dMQ0evSEjmZW;zY{=hX#S`lnh*LeKB_waKy@<# zQ2;Mh`Cjj{zzE(tluLse5-39qgKsTTVa0NgFxB^H3Hvsy* zr8{5)Knk!Bum~UxkO9a776aq}@&E;ZB0veC3{U~60@MIY0O|nrx0BG{IhqHU3lIX# z2Jiy-0Q>;7#wP$pfMP%i;3S|FPzJ~Yv;nRHt^qs%=(o)x07n4&{q!2ZT7V`%3!n`U z1?&Kn11bO=fOY@{U<0rPtOKCmx32_P0;~W!0Q9$b^Z*6`L%?!?5nwkU4zLU01@H#= z0DJ*{0Bb-zU<)7|fPUlS4?w>ei3Ds1px@^N0H}aKz4gz1_l5cYX!grU<^QWMQbGpK=#Z6pmpK{ zpmk#dumTtXC>Bv%G6ALmSO6RV#F+^|@yQNAYmVZP6Tk)F0ibc|$Hh0)ANd*iTOXhY zKx=^h))HC|w4R8I)>Z?6VoDvb1RxKP1I!1YG3Eh;0Fr=30AT=%WmHCSEddY%hyz3b zqJRbT*QgG)p}q*%YZU*B0n+q(S;$eHB0vG43Qz$k1C#)20AweMNAwy$e`6mQJXWs0k95W1wb)s4Oj)R1)!K;4Oj!P0U(YYz!BgCSPyUkcmqfP zPXG}>0c-%c15iDxbEW5G$lU-N0UiK90OBLA7d=Puj^ZATw-=BIhy&~d>;S|9VgS*A z?EoqO#rkKL+Z9 z%4j_NYov?S|24<=+Xej+0DAzt0r3D74|on{H#VOnXrBX^1z431|AQ1*I+@SVc2r*C z9HaqP(Iwxn5lVl)iq3#zB3m+Yv1}~am5aeeORgZwiVJ=0MN1NnQnUP-L;eeDd1THfMWwQlLdcUQJ$Q@+_MPElCOv2~t%$ zOOcu)45UQ6g0e%)(g}y;AmSG%BP$>b`iLkpVe z?f}mV&SuY>X%;9W@1Vb%zzHpfSH$ zLG!T+k;zuOQTi2HAadm4xZeUTXfG%*B&WKx5yxm&!(uBFd?-F-IX72AV^jW{FU?1N z85xx1)o9+)Wu9D9-q5W=5;^^uU;$EtasIj&xW3Ql3d`g;9@LZHK5-UNc)!QNO=v+8 za$Svgt$p9vbY6yKI>_0|w5#f$BN6k?CiNeljS6`%!HVJTq`sG;?#wy_s@tHXJLrnVHRr9cbvj_6$lmxV5(Phmc4 zlNKC-78D`jD~Ehjrhn`Mjxu-$I3@I!dE#%|^ObBpfCEv0TFycX+QD3xN;(?9q<*1| zvjpwaW_rtxof&D#PyDi>1&srHp^M(qd&kn_5s!2`Vf^D2&rf?q6&bKeng7$(?ExXR}*wzM`1++8d8SA7Eq{JTP z*A?i*0EcdYC)FRmG!J)2hOQk&bF)s-T9ncHP^ljNUJxO<{?9U%W{p#6EwuF^Qhi~* za7P%%W?>gp=mz6J7^0c}^Ehrqss}6*%a0X|-e( ztGmKAXR-xtZZe4^hj{GA#$OJM3mVWlFwRc;II|ebZwELQl+kRX?bH8ML$;~HRR7t6 zzqOjSKL6~Ue{T6tHCn{OO#hQZUsA$l2zRs>R3B0cnS;m4=tQC&{)8LQg7(7eiI8lK zSU+1v2KvEA2qp&v_yxdUW*stE!rtLpf;dVvAN=PySnP1a;-K}Z-g)GVkzZT^;?S1o zZ!7=~Ev|{4=-!J!cxQEhL$A+FAAAkF2L+EKY%657icJmW<|U;F`f9^Wg90e9a&R}z z9@*?9yL$(;sDc{A!FAxEy&%|<#C9*XZ1M_7i=7A10(U8qy>U`smpR6<0S8SLR{Jfq z%!QU1-J3#Y*IP{f7{@=zcQeV)jV!&)b(cB^voo!QwsL=ahR}AFKh>KypRRm+(%77# z6~KY<<&`xE|DXmI{CBTE#DI}#UeTf=7aq@=i-EWO>k{Cb{_&Qe{&arE?NIH4y+LDdWuf~5pJ?ijE8x@OEg~( z;~rUzad4*WS_yS|CJK#T3CbRKOTfEM~;6)QMt!QsOFFZua8W1$7^LQuTzhZf}P ztt6P<6cOa5l2aFV0F{WmlH zXRGO}@HaC>KKT2Y(m8)K)1{Nw1sQnwg4Wc&dCBS*W*>)OK9eE(_a_By*OxD#Js*n9 z^4&Gu_1^Ur%?GM*xDYNu3pc2>sos@$>|_b1`3=qxWkNT!pms#}`n{$(bOLu(nN zx0KX8@L;&yfv#ETE)6*Epaty`?P>`zsa=eXv=(LgrSLRMWK!*NeQKGX#G=j6f_90L zB7qlLc!0C?PU_4($IW&AP?LfdWShgtkky)2E)KdHtdBZf%_o0Du4QA75?u}EV?u8c zsF*nEu*DFaRp?zcv>b#McGx7d%~eZb10+vovkL2)g+H-k_jb`WupcOc&WjcrH8COv9tvSUm@m}50<99kUg zBTSBie_ua&gMAt}XtmvUB)t%NcOeKk^!@YCGrbHPS z{0Ho3QFjJjg%VZvXwUVMIEtg&T`OrKk&6HR4#jk;2rLkT%$llh-N ztfi8D;O(Ql5j-9W@FjY$2@Ih4Zd@j!^vC074cy~qPpY3Uy1%vSj}vWa;WlMqnS7VR zV=H!y|HWM2zdj@VYx`SxmVwqieyg2sviQ+}FHlC;tZ$j^@~`#@zL^P=L+uyIWuh!? zk6-ga8MQy^Cnla<=6*^X%IHLXbZhPTRqlJd6`;%o<)aG@GRH_8g#=Pbv`N-Y@E#2~ zu#eZ3;;Vq_IPd3akIdgx6G)|!17N<+T_RDd-Oa}jf~S#QjE34(hER^L*Xceg>BW47 zp1mvm)1!An;AVd^l@vhn57eW*RB#RQ)uZ@s3Zev%0~GuN{CxZasd`>PuHalUJbqUo zc~i)~fpWg^PSqnY(4VTQpb$Xb2oGBWHp~0^`*>32{Q@>B`~yQl4$qgPU0W6912=}z z2M?jrUm$r3)z8N-z#raN$c0d`b(8b=4#GOopS9B|^ylmJ2Y#+W?(XCOs-HjEm*OTj zY4i{5{Zl`%@=s+sHF)}_hRpO12$CZaNgiZBS5Go2P|gFMDfv;{NOFOGUSwZd@kfLF zxhERt&owX*dPfNZ{Yd4Hp^?HLB{_H~0sJ4czs~@;-(SnoZuoz~q`#X69{rUZ?V|h> z7JA|j6qG@Kc+|i2Cxkz*A5i~XA&2gHe&(V-M+9ilV*@;-3ZMEYBNN>H0(|7$h=D|U zXY3&|y%^{hw9!M3lkIj~2--YN3Yt^qE} zFLna`FEt>eD6fH3+{oU< z&EIGA1K<9+BS`+af~NShy^B4$12*;&!UOF>u;d40)1!-iTmqZ^iWZu*{fMqI-%nUL zEag3^KhXQ$6P?90xyfrP?QE8V4fa!u{&cj1!k@~tQ}AbV3PA&hP|;}Ui48bl^284c zq<9Afy31+E1^=IB&ThGJ8-~KKk{3|3$+q2Tr|slbXjx9Qu`RD;H_6OCe7^&dBBf18 z_ik?@K;i*G5(Iw$hfFQz_Z71@$-+?0ITRWng0<8hDA`oiBLxQ}ln(=Az{Gy7GJ;{v zL{i!P;4WOeiJaW7YkY%pUvj=2kKMj;kF3DVua13yXV|93_6}n|*-YQMaddBLiHUUQ z%h;W|QS;Ir+m03dMFXkH$cCxky)~+}(DHXm)1G(U+5p;&FGyRdf~3_Oo8mPyST+wK z%NLAISQ5EZ?#8oOsD+kr?ZG?blv&r+Wn>--%hBvdEV{{GV@}H&RO#yHRCP+<``ZC8cA?;YE4|{+j*^Z;ONlkMq-l zpcdeiy7tj)Ig7=?aXvMxZ=yKFbr7C$2h|Sk5RAds55q zRX;=ismilhe4(?=3LpB(xpunUO-;;*D2&&S7c-i0W|Uj*-&YJC**GK2~#8VP6BchkYD=4!Ce2%mR>6i@ggL~UZ}pQ{4VqhbFNHnCq_`#haG z_bi1D?%Y0ELc`Qv#_>&_^>G}y+i2RSe!o3YnDZ1FKI|fPTz587x}fO$0JM1j!&MuK zwNgl1zoJva#;P5Mi38)wy&QEqar>rw?MoVGr4XEPx{#|nudF)I8Hc%so4Dzz+qmN+ zN2&uy%dK5MSw4t&H;FHFtCgn$d)n-*?E)>Y1cW^eQ8Sm7p6ZOH=1EHYfU$J11+^FD zGJ%G(iBMB2CffjxBslzN#W`g*9iiUB<%0v<(ltKaeE)u9$<+5oY1lp$nl#@EBX*|! z^r|h9(OH3vBvw*i=;nzUfHr9xE#|h7-i(g>ithE~u=wFv(0x@BajQ2r!q&ALx7Rhu zwz0}eG8Y9Ua`D0@c+FO4mjryK8RnJ0=jQFwKb)Q)@z|DC$8uG2n(LxSpJCc5)Ms~G zX{=`8_T*A69K!tFQ1jAn&rbw*{>=FY5(ZdB^H&W^jrdcek%|D%J(vmJrOzt-GP+cD zW*&Ts+RUqDVSwgx=s(luoowz&6^=M>WQZ4pB)yQ57>kUUy_lGqJ4l6)k{G|5RVK75 z#fe&92OT1dqt+>c-AZ>DzV?l&!MSyJ+3&49u9qwS)4&Blp^YCPsO*C;S2?guB_w5> znNiLCWup_j%#RgqjtkzfA4a>c;|8zLyf!a~umztczJGmmhtgeVDl`icLcWzrTi!O# zHVAa4ueugbb_X;u=0mUQNHYwundw;`TSeoN>FGMd+*`-Zf}47GG>a>Y!?GaSEMMT5 zt~kLYJC}gOu_`FcSssCpjY_#kwUyPUCsVB_u9^hVk7he|1Y{iMTt9I95O_yM2*Kk* zKKqbUD#V>v`@9)mvp2(W*N=zvqTBZ8Va#|fqT9sdq%Y^h9xn$1{fSpD_RBD4fYH6^ zmsP3A)VZ;J%K1H!@q&4*i_%8@e1~Ydr2%SaQxV+_CT`b162zF&3q=oYu*qR?Br?1W z?Rl`dJ5?5qRY{asy+ND$#wD*C5jkuA%M4XYHamX$SLl?v3{qxVGo^k{wd{?E*BzwQ zez=_NhL2xC73$AQAtB?gI=>_p!U!QFP=>F?X%k-K4cM-PeeS2(gSuqO)e0fa*}bxF z;YidI9vdgoH6JwtscfcM?CYLsmM%48O$>H@ua)Zk1WTW`&rH!_akluQY$oO7boT5uPv;s|9$Hi3JY^ zJWzDLdg0pIqfbOYB)zYwj^Eq>?`9F;32OlN7uRZFP8+m6P5W9>O=`WJqWsmMR)YVl zxnEDH+Y=dFSB=WQYy6jjHLOZX*b-~!U+a!uQQBtT7o+$jTq1+NW=5ZFpF9u)Kb*Gx zSkPlG1A3-yENUZ})B>6q>Q@G+#_^xOZ*2Xz`RnF$L4}ZCij+VhqJE#8Mw9N*dM~`E ztn2P;`ey>d7Q!^}y9vgWZ2X!rdXG%9AV~hwZFfEZCB{A$nmv~ZU@LDT?yO?n2ggLm Yf*6BY?jmv$!&d1xKc9UH { + const kc = new k8s.KubeConfig() + kc.loadFromDefault() + + const k8sApi = kc.makeApiClient(k8s.CustomObjectsApi) + + try { + const res = await k8sApi.listNamespacedCustomObject('hlf.kungfusoftware.es', 'v1alpha1', namespace, 'fabricorderernodes') + const ordererList = (res.body as any).items + return ordererList.filter((orderer: any) => orderer.spec.image.tag !== '3.0.0-beta') + } catch (err) { + console.error('Error fetching orderers from cluster:', err) + return [] + } +} + +async function getOrderersFromCluster(namespace: string): Promise { + const kc = new k8s.KubeConfig() + kc.loadFromDefault() + + const k8sApi = kc.makeApiClient(k8s.CustomObjectsApi) + + try { + const res = await k8sApi.listNamespacedCustomObject('hlf.kungfusoftware.es', 'v1alpha1', namespace, 'fabricorderernodes') + const ordererList = (res.body as any).items + return ordererList + } catch (err) { + console.error('Error fetching orderers from cluster:', err) + return [] + } +} + +async function updateOrderers(orderers: { name: string; namespace: string }[]) { + for (const orderer of orderers) { + await updateOrdererTag([orderer.name], orderer.namespace) + console.log(`Waiting for orderer ${orderer.name} in namespace ${orderer.namespace} to be ready...`) + // Add logic here to wait for the orderer to be ready + // Wait for the orderer to be ready with the new tag + let ready = false + const maxWaitTime = 10 * 60 * 1000 // 10 minutes in milliseconds + const pollInterval = 10000 // 10 seconds + + const startTime = Date.now() + + while (!ready && Date.now() - startTime < maxWaitTime) { + try { + const appsV1Api = kc.makeApiClient(k8s.AppsV1Api) + const res = await appsV1Api.readNamespacedDeployment(orderer.name, orderer.namespace) + const deployment = res.body + + const hasCorrectTag = deployment.spec?.template.spec?.containers.some((container) => container.image?.includes('3.0.0-beta')) + const isReady = + deployment.status?.conditions?.some((condition) => condition.type === 'Available' && condition.status === 'True') && + deployment.status?.readyReplicas === deployment.status?.replicas + + if (hasCorrectTag && isReady) { + ready = true + console.log(`Orderer ${orderer.name} in namespace ${orderer.namespace} is ready with tag 3.0.0-beta`) + } else { + const elapsedTime = Math.floor((Date.now() - startTime) / 1000) + console.log(`Waiting for orderer ${orderer.name} in namespace ${orderer.namespace} to be ready (${elapsedTime} seconds elapsed)...`) + await new Promise((resolve) => setTimeout(resolve, pollInterval)) + } + } catch (err) { + console.error(`Error checking orderer ${orderer.name} in namespace ${orderer.namespace} status:`, err) + await new Promise((resolve) => setTimeout(resolve, pollInterval)) + } + } + + if (!ready) { + console.error(`Orderer ${orderer.name} in namespace ${orderer.namespace} did not become ready within the expected time.`) + } + } +} + +async function waitForChannelConsensusTypeBFT(channelName: string) { + console.log(`Waiting for ${channelName} ConsensusType to be BFT...`) + const kc = new k8s.KubeConfig() + kc.loadFromDefault() + const k8sApi = kc.makeApiClient(k8s.CoreV1Api) + + const maxWaitTime = 10 * 60 * 1000 // 10 minutes in milliseconds + const pollInterval = 1000 // 1 seconds + const startTime = Date.now() + + while (Date.now() - startTime < maxWaitTime) { + try { + const res = await k8sApi.readNamespacedConfigMap(`${channelName}-config`, 'default') + const configMap = res.body + const channelJson = JSON.parse(configMap.data!['channel.json']) + const consensusType = channelJson.channel_group.groups.Orderer.values.ConsensusType.value.type + + if (consensusType === 'BFT') { + console.log(`Channel ${channelName} ConsensusType is now BFT`) + return + } + + console.log(`Waiting for ${channelName} ConsensusType to be BFT. Current type: ${consensusType}`) + await new Promise((resolve) => setTimeout(resolve, pollInterval)) + } catch (err) { + console.error(`Error checking ${channelName}-config configmap:`, err) + await new Promise((resolve) => setTimeout(resolve, pollInterval)) + } + } + + console.error(`Timeout: ${channelName} ConsensusType did not change to BFT within 10 minutes`) + throw new Error(`Timeout waiting for ${channelName} ConsensusType to be BFT`) +} + +async function waitForChannelStateUpdate(channelName: string, expectedState: string) { + console.log(`Waiting for ${channelName} to be updated...`) + const kc = new k8s.KubeConfig() + kc.loadFromDefault() + const k8sApi = kc.makeApiClient(k8s.CoreV1Api) + + const maxWaitTime = 5 * 60 * 1000 // 5 minutes in milliseconds + const pollInterval = 1000 // 1 second + const startTime = Date.now() + + while (Date.now() - startTime < maxWaitTime) { + try { + const res = await k8sApi.readNamespacedConfigMap(`${channelName}-config`, 'default') + const configMap = res.body + const channelJson = JSON.parse(configMap.data!['channel.json']) + const state = channelJson.channel_group.groups.Orderer.values.ConsensusType.value.state + + if (state === expectedState) { + console.log(`Channel ${channelName} is now in ${expectedState}`) + return + } + + console.log(`Waiting for ${channelName} to be in ${expectedState}...`) + await new Promise((resolve) => setTimeout(resolve, pollInterval)) + } catch (err) { + console.error(`Error checking ${channelName}-config configmap:`, err) + await new Promise((resolve) => setTimeout(resolve, pollInterval)) + } + } + + console.error(`Timeout: ${channelName} did not enter STATE_MAINTENANCE within 5 minutes`) + // Add logic here to check the ${channel}-config configmap +} + +async function setFabricMainChannelToNormal(channelName: string, namespace: string = '') { + try { + console.log(`Setting ${channelName} to STATE_NORMAL...`) + + const kc = new k8s.KubeConfig() + kc.loadFromDefault() + + const k8sApi = kc.makeApiClient(k8s.CustomObjectsApi) + + // Fetch the current FabricMainChannel object + const res = await k8sApi.getNamespacedCustomObject('hlf.kungfusoftware.es', 'v1alpha1', namespace, 'fabricmainchannels', channelName) + + const channel = res.body as any + + // Update the orderer state to STATE_NORMAL + if (channel.spec && channel.spec.channelConfig && channel.spec.channelConfig.orderer) { + channel.spec.channelConfig.orderer.state = 'STATE_NORMAL' + } else { + console.error(`Unable to update state for channel ${channelName}: channelConfig.orderer not found`) + return + } + + // Update the FabricMainChannel + await k8sApi.patchNamespacedCustomObject('hlf.kungfusoftware.es', 'v1alpha1', namespace, 'fabricmainchannels', channelName, channel, undefined, undefined, undefined, { + headers: { 'Content-Type': 'application/merge-patch+json' }, + }) + + // Wait for the channel to be updated + await waitForChannelStateUpdate(channelName, 'STATE_NORMAL') + console.log(`Successfully set ${channelName} to STATE_NORMAL`) + } catch (err) { + console.error(`Error setting ${channelName} to STATE_NORMAL:`, err) + throw err + } +} + +async function setFabricMainChannelToMaintenance(channelName: string, namespace: string = '') { + try { + console.log(`Setting ${channelName} to STATE_MAINTENANCE...`) + + const kc = new k8s.KubeConfig() + kc.loadFromDefault() + + const k8sApi = kc.makeApiClient(k8s.CustomObjectsApi) + + // Fetch the current FabricMainChannel object + const res = await k8sApi.getNamespacedCustomObject('hlf.kungfusoftware.es', 'v1alpha1', namespace, 'fabricmainchannels', channelName) + + const channel = res.body as any + + // Update the orderer state to STATE_MAINTENANCE + if (channel.spec && channel.spec.channelConfig && channel.spec.channelConfig.orderer) { + channel.spec.channelConfig.orderer.state = 'STATE_MAINTENANCE' + } else { + console.error(`Unable to update state for channel ${channelName}: channelConfig.orderer not found`) + return + } + + // Update the FabricMainChannel + await k8sApi.patchNamespacedCustomObject('hlf.kungfusoftware.es', 'v1alpha1', namespace, 'fabricmainchannels', channelName, channel, undefined, undefined, undefined, { + headers: { 'Content-Type': 'application/merge-patch+json' }, + }) + // wait for the channel to be updated + await waitForChannelStateUpdate(channelName, 'STATE_MAINTENANCE') + console.log(`Successfully set ${channelName} to STATE_MAINTENANCE`) + } catch (err) { + console.error(`Error setting ${channelName} to STATE_MAINTENANCE:`, err) + throw err + } +} + +async function getFabricOrdererNode(ordererName: string, namespace: string = 'default'): Promise { + try { + console.log(`Fetching FabricOrdererNode ${ordererName} from namespace ${namespace}...`) + + const kc = new k8s.KubeConfig() + kc.loadFromDefault() + + const k8sApi = kc.makeApiClient(k8s.CustomObjectsApi) + + const res = await k8sApi.getNamespacedCustomObject('hlf.kungfusoftware.es', 'v1alpha1', namespace, 'fabricorderernodes', ordererName) + + const fabricOrdererNode = res.body as any + + if (!fabricOrdererNode.status || !fabricOrdererNode.status.signCert) { + throw new Error(`FabricOrdererNode ${ordererName} does not have a signCert in its status`) + } + + console.log(`Successfully fetched FabricOrdererNode ${ordererName}`) + return fabricOrdererNode + } catch (err) { + console.error(`Error fetching FabricOrdererNode ${ordererName}:`, err) + throw err + } +} + +async function getChannelFromKubernetes(channelName: string): Promise { + try { + console.log(`Fetching channel ${channelName} from Kubernetes...`) + + const kc = new k8s.KubeConfig() + kc.loadFromDefault() + + const customApi = kc.makeApiClient(k8s.CustomObjectsApi) + + const group = 'hlf.kungfusoftware.es' + const version = 'v1alpha1' + const plural = 'fabricmainchannels' + + const response = await customApi.getNamespacedCustomObject(group, version, '', plural, channelName) + + console.log(`Successfully fetched channel ${channelName}`) + return response.body + } catch (error) { + console.error(`Error fetching channel ${channelName}:`, error) + throw error + } +} + +async function updateChannelToBFT(channelName: string): Promise { + try { + console.log(`Updating channel ${channelName} to use BFT consensus...`) + const orderers = await getOrderersFromCluster('') + // Fetch the current channel configuration + const channel = await getChannelFromKubernetes(channelName) + console.log(channel) + // Update the consensus type to BFT + if (channel.spec && channel.spec.channelConfig) { + channel.spec.channelConfig.capabilities = ['V3_0'] + channel.spec.channelConfig.application.capabilities = ['V3_0'] + channel.spec.channelConfig.orderer.ordererType = 'BFT' + // go through channel.spec.orderers and ask either for the orderer name or the namespace (radio, select one), or ask for the identity file path to get the certificate from + const consenterMapping = [] + let idx = 0 + for (const orderer of channel.spec.orderers) { + const selectedOrderer = await select({ + message: `Select the orderer ${orderer.name} (${orderer.namespace}) for the consenter ${orderer.host}:${orderer.port}`, + choices: [...orderers.map((orderer) => ({ name: orderer.metadata.name, value: orderer.metadata.name })), { name: 'Identity file path', value: 'identity' }], + }) + let identityCert = '' + let mspId = '' + if (selectedOrderer === 'identity') { + const identity = await input({ message: 'Enter the identity file path:' }) + identityCert = (await readFile(identity)).toString('utf-8') + // ask for the mspId + mspId = await input({ message: 'Enter the mspId:' }) + } else { + // get fabricorderernode and get the identity cert from `status.signCert` + const fabricOrdererNode = await getFabricOrdererNode(selectedOrderer) + identityCert = fabricOrdererNode.status.signCert + mspId = fabricOrdererNode.spec.mspID + } + if (!identityCert) { + throw new Error(`Identity cert not found for orderer ${selectedOrderer}`) + } + if (!mspId) { + throw new Error(`MspId not found for orderer ${selectedOrderer}`) + } + consenterMapping.push({ + client_tls_cert: orderer.tlsCert, + host: orderer.host, + id: idx, + identity: identityCert, + msp_id: mspId, + port: orderer.port, + server_tls_cert: orderer.tlsCert, + }) + } + channel.spec.channelConfig.orderer.consenterMapping = consenterMapping + channel.spec.channelConfig.orderer.smartBFT = { + collectTimeout: '1s', + decisionsPerLeader: 3, + incomingMessageBufferSize: 200, + leaderHeartbeatCount: 10, + leaderHeartbeatTimeout: '1m0s', + leaderRotation: 2, + requestAutoRemoveTimeout: '3m', + requestBatchMaxBytes: 10485760, + requestBatchMaxCount: 100, + requestBatchMaxInterval: '50ms', + requestComplainTimeout: '20s', + requestForwardTimeout: '2s', + requestMaxBytes: 10485760, + requestPoolSize: 100000, + speedUpViewChange: false, + syncOnStart: true, + viewChangeResendInterval: '5s', + viewChangeTimeout: '20s', + } + } else { + console.error(`Channel ${channelName} configuration is not in the expected format.`) + throw new Error('Invalid channel configuration') + } + + // Update the FabricMainChannel resource + const kc = new k8s.KubeConfig() + kc.loadFromDefault() + const k8sApi = kc.makeApiClient(k8s.CustomObjectsApi) + + await k8sApi.patchNamespacedCustomObject('hlf.kungfusoftware.es', 'v1alpha1', '', 'fabricmainchannels', channelName, channel, undefined, undefined, undefined, { + headers: { 'Content-Type': 'application/merge-patch+json' }, + }) + await waitForChannelConsensusTypeBFT(channelName) + console.log(`Successfully updated channel ${channelName} to use BFT consensus`) + } catch (error) { + console.error(`Error updating channel ${channelName} to BFT:`, error) + throw error + } +} + +async function main() { + const channelName = await input({ message: 'Enter the channel name:' }) + const channel = await getChannelFromKubernetes(channelName) + console.log(channel) + // const ordererNamesInput = await input({ message: 'Enter orderer names (comma-separated):' }) + const ordererList = await getOrderersFromClusterBelow30('') + const selectedOrderers = await checkbox({ + message: 'What orderers do you want to upgrade to 3.0.0-beta?', + choices: ordererList.map((orderer: any) => ({ + name: orderer.metadata.name, + value: { + name: orderer.metadata.name, + namespace: orderer.metadata.namespace, + }, + checked: true, + })), + }) + // console.log('selectedOrderers', selectedOrderers) + // ask for confirmation on to upgrade the selected orderers + const confirmed = await confirm({ + message: `Upgrade the following orderers to version 3.0.0-beta?\n${selectedOrderers.map((orderer) => `- ${orderer.name} (${orderer.namespace})`).join('\n')}`, + default: true, + }) + if (confirmed) { + console.log('Upgrading the selected orderers...') + await updateOrderers(selectedOrderers) + } + // confirm set channel to maintenance + const stateConfirmed = await confirm({ + message: `Set channel ${channelName} to STATE_MAINTENANCE?`, + default: true, + }) + if (stateConfirmed) { + await setFabricMainChannelToMaintenance(channelName) + } + const bftConfirmed = await confirm({ + message: `Update channel ${channelName} to use BFT consensus?`, + default: true, + }) + if (bftConfirmed) { + await updateChannelToBFT(channelName) + } + + const stateNormalConfirmed = await confirm({ + message: `Set channel ${channelName} to STATE_NORMAL?`, + default: true, + }) + if (stateNormalConfirmed) { + await setFabricMainChannelToNormal(channelName) + } +} + +main().catch(console.error) + +// 1. Ask for backup of the orderers +// 2. Update the orderers to the version 3.0.0-beta one by one and wait for the orderers to be ready +// 3. Set channel to STATE_MAINTENANCE +// 4. Wait for the channel to be updated by checking the ${channel}-config configmap +// 5. Add consenter_mapping to the channel and update the capabilities +// 6. Set channel to STATE_NORMAL +// 7. Wait for the channel to be updated by checking the ${channel}-config configmap +// 8. Migration completed :) diff --git a/scripts/package.json b/scripts/package.json new file mode 100644 index 00000000..bf1486b5 --- /dev/null +++ b/scripts/package.json @@ -0,0 +1,15 @@ +{ + "name": "scripts", + "module": "index.ts", + "type": "module", + "devDependencies": { + "@types/bun": "latest" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "dependencies": { + "@inquirer/prompts": "^5.3.8", + "@kubernetes/client-node": "^0.21.0" + } +} \ No newline at end of file diff --git a/scripts/tsconfig.json b/scripts/tsconfig.json new file mode 100644 index 00000000..238655f2 --- /dev/null +++ b/scripts/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + // Enable latest features + "lib": ["ESNext", "DOM"], + "target": "ESNext", + "module": "ESNext", + "moduleDetection": "force", + "jsx": "react-jsx", + "allowJs": true, + + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false + } +}