PNG  IHDRX cHRMz&u0`:pQ<bKGD pHYsodtIME MeqIDATxw]Wug^Qd˶ 6`!N:!@xI~)%7%@Bh&`lnjVF29gΨ4E$|>cɚ{gk= %,a KX%,a KX%,a KX%,a KX%,a KX%,a KX%, b` ǟzeאfp]<!SJmɤY޲ڿ,%c ~ع9VH.!Ͳz&QynֺTkRR.BLHi٪:l;@(!MԴ=žI,:o&N'Kù\vRmJ雵֫AWic H@" !: Cé||]k-Ha oݜ:y F())u]aG7*JV@J415p=sZH!=!DRʯvɱh~V\}v/GKY$n]"X"}t@ xS76^[bw4dsce)2dU0 CkMa-U5tvLƀ~mlMwfGE/-]7XAƟ`׮g ewxwC4\[~7@O-Q( a*XGƒ{ ՟}$_y3tĐƤatgvێi|K=uVyrŲlLӪuܿzwk$m87k( `múcE)"@rK( z4$D; 2kW=Xb$V[Ru819קR~qloѱDyįݎ*mxw]y5e4K@ЃI0A D@"BDk_)N\8͜9dz"fK0zɿvM /.:2O{ Nb=M=7>??Zuo32 DLD@D| &+֎C #B8ַ`bOb $D#ͮҪtx]%`ES`Ru[=¾!@Od37LJ0!OIR4m]GZRJu$‡c=%~s@6SKy?CeIh:[vR@Lh | (BhAMy=݃  G"'wzn޺~8ԽSh ~T*A:xR[ܹ?X[uKL_=fDȊ؂p0}7=D$Ekq!/t.*2ʼnDbŞ}DijYaȲ(""6HA;:LzxQ‘(SQQ}*PL*fc\s `/d'QXW, e`#kPGZuŞuO{{wm[&NBTiiI0bukcA9<4@SӊH*؎4U/'2U5.(9JuDfrޱtycU%j(:RUbArLֺN)udA':uGQN"-"Is.*+k@ `Ojs@yU/ H:l;@yyTn}_yw!VkRJ4P)~y#)r,D =ě"Q]ci'%HI4ZL0"MJy 8A{ aN<8D"1#IJi >XjX֔#@>-{vN!8tRݻ^)N_╗FJEk]CT՟ YP:_|H1@ CBk]yKYp|og?*dGvzنzӴzjֺNkC~AbZƷ`.H)=!QͷVTT(| u78y֮}|[8-Vjp%2JPk[}ԉaH8Wpqhwr:vWª<}l77_~{s۴V+RCģ%WRZ\AqHifɤL36: #F:p]Bq/z{0CU6ݳEv_^k7'>sq*+kH%a`0ԣisqにtү04gVgW΂iJiS'3w.w}l6MC2uԯ|>JF5`fV5m`Y**Db1FKNttu]4ccsQNnex/87+}xaUW9y>ͯ骵G{䩓Գ3+vU}~jJ.NFRD7<aJDB1#ҳgSb,+CS?/ VG J?|?,2#M9}B)MiE+G`-wo߫V`fio(}S^4e~V4bHOYb"b#E)dda:'?}׮4繏`{7Z"uny-?ǹ;0MKx{:_pÚmFמ:F " .LFQLG)Q8qN q¯¯3wOvxDb\. BKD9_NN &L:4D{mm o^tֽ:q!ƥ}K+<"m78N< ywsard5+вz~mnG)=}lYݧNj'QJS{S :UYS-952?&O-:W}(!6Mk4+>A>j+i|<<|;ر^߉=HE|V#F)Emm#}/"y GII웻Jі94+v뾧xu~5C95~ūH>c@덉pʃ1/4-A2G%7>m;–Y,cyyaln" ?ƻ!ʪ<{~h~i y.zZB̃/,雋SiC/JFMmBH&&FAbϓO^tubbb_hZ{_QZ-sύodFgO(6]TJA˯#`۶ɟ( %$&+V'~hiYy>922 Wp74Zkq+Ovn錄c>8~GqܲcWꂎz@"1A.}T)uiW4="jJ2W7mU/N0gcqܗOO}?9/wìXžΏ0 >֩(V^Rh32!Hj5`;O28؇2#ݕf3 ?sJd8NJ@7O0 b־?lldщ̡&|9C.8RTWwxWy46ah嘦mh٤&l zCy!PY?: CJyв]dm4ǜҐR޻RլhX{FƯanшQI@x' ao(kUUuxW_Ñ줮[w8 FRJ(8˼)_mQ _!RJhm=!cVmm ?sFOnll6Qk}alY}; "baӌ~M0w,Ggw2W:G/k2%R,_=u`WU R.9T"v,<\Ik޽/2110Ӿxc0gyC&Ny޽JҢrV6N ``یeA16"J³+Rj*;BϜkZPJaÍ<Jyw:NP8/D$ 011z֊Ⱳ3ι֘k1V_"h!JPIΣ'ɜ* aEAd:ݺ>y<}Lp&PlRfTb1]o .2EW\ͮ]38؋rTJsǏP@芎sF\> P^+dYJLbJ C-xϐn> ι$nj,;Ǖa FU *择|h ~izť3ᤓ`K'-f tL7JK+vf2)V'-sFuB4i+m+@My=O҈0"|Yxoj,3]:cо3 $#uŘ%Y"y죯LebqtҢVzq¼X)~>4L׶m~[1_k?kxֺQ`\ |ٛY4Ѯr!)N9{56(iNq}O()Em]=F&u?$HypWUeB\k]JɩSع9 Zqg4ZĊo oMcjZBU]B\TUd34ݝ~:7ڶSUsB0Z3srx 7`:5xcx !qZA!;%͚7&P H<WL!džOb5kF)xor^aujƍ7 Ǡ8/p^(L>ὴ-B,{ۇWzֺ^k]3\EE@7>lYBȝR.oHnXO/}sB|.i@ɥDB4tcm,@ӣgdtJ!lH$_vN166L__'Z)y&kH;:,Y7=J 9cG) V\hjiE;gya~%ks_nC~Er er)muuMg2;֫R)Md) ,¶ 2-wr#F7<-BBn~_(o=KO㭇[Xv eN_SMgSҐ BS헃D%g_N:/pe -wkG*9yYSZS.9cREL !k}<4_Xs#FmҶ:7R$i,fi!~' # !6/S6y@kZkZcX)%5V4P]VGYq%H1!;e1MV<!ϐHO021Dp= HMs~~a)ަu7G^];git!Frl]H/L$=AeUvZE4P\.,xi {-~p?2b#amXAHq)MWǾI_r`S Hz&|{ +ʖ_= (YS(_g0a03M`I&'9vl?MM+m~}*xT۲(fY*V4x@29s{DaY"toGNTO+xCAO~4Ϳ;p`Ѫ:>Ҵ7K 3}+0 387x\)a"/E>qpWB=1 ¨"MP(\xp߫́A3+J] n[ʼnӼaTbZUWb={~2ooKױӰp(CS\S筐R*JغV&&"FA}J>G֐p1ٸbk7 ŘH$JoN <8s^yk_[;gy-;߉DV{c B yce% aJhDȶ 2IdйIB/^n0tNtџdcKj4϶v~- CBcgqx9= PJ) dMsjpYB] GD4RDWX +h{y`,3ꊕ$`zj*N^TP4L:Iz9~6s) Ga:?y*J~?OrMwP\](21sZUD ?ܟQ5Q%ggW6QdO+\@ ̪X'GxN @'4=ˋ+*VwN ne_|(/BDfj5(Dq<*tNt1х!MV.C0 32b#?n0pzj#!38}޴o1KovCJ`8ŗ_"]] rDUy޲@ Ȗ-;xџ'^Y`zEd?0„ DAL18IS]VGq\4o !swV7ˣι%4FѮ~}6)OgS[~Q vcYbL!wG3 7띸*E Pql8=jT\꘿I(z<[6OrR8ºC~ډ]=rNl[g|v TMTղb-o}OrP^Q]<98S¤!k)G(Vkwyqyr޽Nv`N/e p/~NAOk \I:G6]4+K;j$R:Mi #*[AȚT,ʰ,;N{HZTGMoּy) ]%dHء9Պ䠬|<45,\=[bƟ8QXeB3- &dҩ^{>/86bXmZ]]yޚN[(WAHL$YAgDKp=5GHjU&99v簪C0vygln*P)9^͞}lMuiH!̍#DoRBn9l@ xA/_v=ȺT{7Yt2N"4!YN`ae >Q<XMydEB`VU}u]嫇.%e^ánE87Mu\t`cP=AD/G)sI"@MP;)]%fH9'FNsj1pVhY&9=0pfuJ&gޤx+k:!r˭wkl03׼Ku C &ѓYt{.O.zҏ z}/tf_wEp2gvX)GN#I ݭ߽v/ .& и(ZF{e"=V!{zW`, ]+LGz"(UJp|j( #V4, 8B 0 9OkRrlɱl94)'VH9=9W|>PS['G(*I1==C<5"Pg+x'K5EMd؞Af8lG ?D FtoB[je?{k3zQ vZ;%Ɠ,]E>KZ+T/ EJxOZ1i #T<@ I}q9/t'zi(EMqw`mYkU6;[t4DPeckeM;H}_g pMww}k6#H㶏+b8雡Sxp)&C $@'b,fPߑt$RbJ'vznuS ~8='72_`{q纶|Q)Xk}cPz9p7O:'|G~8wx(a 0QCko|0ASD>Ip=4Q, d|F8RcU"/KM opKle M3#i0c%<7׿p&pZq[TR"BpqauIp$ 8~Ĩ!8Սx\ւdT>>Z40ks7 z2IQ}ItԀ<-%S⍤};zIb$I 5K}Q͙D8UguWE$Jh )cu4N tZl+[]M4k8֦Zeq֮M7uIqG 1==tLtR,ƜSrHYt&QP윯Lg' I,3@P'}'R˪e/%-Auv·ñ\> vDJzlӾNv5:|K/Jb6KI9)Zh*ZAi`?S {aiVDԲuy5W7pWeQJk֤#5&V<̺@/GH?^τZL|IJNvI:'P=Ϛt"¨=cud S Q.Ki0 !cJy;LJR;G{BJy޺[^8fK6)=yʊ+(k|&xQ2`L?Ȓ2@Mf 0C`6-%pKpm')c$׻K5[J*U[/#hH!6acB JA _|uMvDyk y)6OPYjœ50VT K}cǻP[ $:]4MEA.y)|B)cf-A?(e|lɉ#P9V)[9t.EiQPDѠ3ϴ;E:+Օ t ȥ~|_N2,ZJLt4! %ա]u {+=p.GhNcŞQI?Nd'yeh n7zi1DB)1S | S#ًZs2|Ɛy$F SxeX{7Vl.Src3E℃Q>b6G ўYCmtկ~=K0f(=LrAS GN'ɹ9<\!a`)֕y[uՍ[09` 9 +57ts6}b4{oqd+J5fa/,97J#6yν99mRWxJyѡyu_TJc`~W>l^q#Ts#2"nD1%fS)FU w{ܯ R{ ˎ󅃏џDsZSQS;LV;7 Od1&1n$ N /.q3~eNɪ]E#oM~}v֯FڦwyZ=<<>Xo稯lfMFV6p02|*=tV!c~]fa5Y^Q_WN|Vs 0ҘދU97OI'N2'8N֭fgg-}V%y]U4 峧p*91#9U kCac_AFңĪy뚇Y_AiuYyTTYЗ-(!JFLt›17uTozc. S;7A&&<ԋ5y;Ro+:' *eYJkWR[@F %SHWP 72k4 qLd'J "zB6{AC0ƁA6U.'F3:Ȅ(9ΜL;D]m8ڥ9}dU "v!;*13Rg^fJyShyy5auA?ɩGHRjo^]׽S)Fm\toy 4WQS@mE#%5ʈfFYDX ~D5Ϡ9tE9So_aU4?Ѽm%&c{n>.KW1Tlb}:j uGi(JgcYj0qn+>) %\!4{LaJso d||u//P_y7iRJ߬nHOy) l+@$($VFIQ9%EeKʈU. ia&FY̒mZ=)+qqoQn >L!qCiDB;Y<%} OgBxB!ØuG)WG9y(Ą{_yesuZmZZey'Wg#C~1Cev@0D $a@˲(.._GimA:uyw֬%;@!JkQVM_Ow:P.s\)ot- ˹"`B,e CRtaEUP<0'}r3[>?G8xU~Nqu;Wm8\RIkբ^5@k+5(By'L&'gBJ3ݶ!/㮻w҅ yqPWUg<e"Qy*167΃sJ\oz]T*UQ<\FԎ`HaNmڜ6DysCask8wP8y9``GJ9lF\G g's Nn͵MLN֪u$| /|7=]O)6s !ĴAKh]q_ap $HH'\1jB^s\|- W1:=6lJBqjY^LsPk""`]w)󭃈,(HC ?䔨Y$Sʣ{4Z+0NvQkhol6C.婧/u]FwiVjZka&%6\F*Ny#8O,22+|Db~d ~Çwc N:FuuCe&oZ(l;@ee-+Wn`44AMK➝2BRՈt7g*1gph9N) *"TF*R(#'88pm=}X]u[i7bEc|\~EMn}P瘊J)K.0i1M6=7'_\kaZ(Th{K*GJyytw"IO-PWJk)..axӝ47"89Cc7ĐBiZx 7m!fy|ϿF9CbȩV 9V-՛^pV̌ɄS#Bv4-@]Vxt-Z, &ֺ*diؠ2^VXbs֔Ìl.jQ]Y[47gj=幽ex)A0ip׳ W2[ᎇhuE^~q흙L} #-b۸oFJ_QP3r6jr+"nfzRJTUqoaۍ /$d8Mx'ݓ= OՃ| )$2mcM*cЙj}f };n YG w0Ia!1Q.oYfr]DyISaP}"dIӗթO67jqR ҊƐƈaɤGG|h;t]䗖oSv|iZqX)oalv;۩meEJ\!8=$4QU4Xo&VEĊ YS^E#d,yX_> ۘ-e\ "Wa6uLĜZi`aD9.% w~mB(02G[6y.773a7 /=o7D)$Z 66 $bY^\CuP. (x'"J60׿Y:Oi;F{w佩b+\Yi`TDWa~|VH)8q/=9!g߆2Y)?ND)%?Ǐ`k/sn:;O299yB=a[Ng 3˲N}vLNy;*?x?~L&=xyӴ~}q{qE*IQ^^ͧvü{Huu=R|>JyUlZV, B~/YF!Y\u_ݼF{_C)LD]m {H 0ihhadd nUkf3oٺCvE\)QJi+֥@tDJkB$1!Đr0XQ|q?d2) Ӣ_}qv-< FŊ߫%roppVBwü~JidY4:}L6M7f٬F "?71<2#?Jyy4뷢<_a7_=Q E=S1И/9{+93֮E{ǂw{))?maÆm(uLE#lïZ  ~d];+]h j?!|$F}*"4(v'8s<ŏUkm7^7no1w2ؗ}TrͿEk>p'8OB7d7R(A 9.*Mi^ͳ; eeUwS+C)uO@ =Sy]` }l8^ZzRXj[^iUɺ$tj))<sbDJfg=Pk_{xaKo1:-uyG0M ԃ\0Lvuy'ȱc2Ji AdyVgVh!{]/&}}ċJ#%d !+87<;qN޼Nفl|1N:8ya  8}k¾+-$4FiZYÔXk*I&'@iI99)HSh4+2G:tGhS^繿 Kتm0 вDk}֚+QT4;sC}rՅE,8CX-e~>G&'9xpW,%Fh,Ry56Y–hW-(v_,? ; qrBk4-V7HQ;ˇ^Gv1JVV%,ik;D_W!))+BoS4QsTM;gt+ndS-~:11Sgv!0qRVh!"Ȋ(̦Yl.]PQWgٳE'`%W1{ndΗBk|Ž7ʒR~,lnoa&:ü$ 3<a[CBݮwt"o\ePJ=Hz"_c^Z.#ˆ*x z̝grY]tdkP*:97YľXyBkD4N.C_[;F9`8& !AMO c `@BA& Ost\-\NX+Xp < !bj3C&QL+*&kAQ=04}cC!9~820G'PC9xa!w&bo_1 Sw"ܱ V )Yl3+ס2KoXOx]"`^WOy :3GO0g;%Yv㐫(R/r (s } u B &FeYZh0y> =2<Ϟc/ -u= c&׭,.0"g"7 6T!vl#sc>{u/Oh Bᾈ)۴74]x7 gMӒ"d]U)}" v4co[ ɡs 5Gg=XR14?5A}D "b{0$L .\4y{_fe:kVS\\O]c^W52LSBDM! C3Dhr̦RtArx4&agaN3Cf<Ԉp4~ B'"1@.b_/xQ} _߃҉/gٓ2Qkqp0շpZ2fԫYz< 4L.Cyυι1t@鎫Fe sYfsF}^ V}N<_`p)alٶ "(XEAVZ<)2},:Ir*#m_YӼ R%a||EƼIJ,,+f"96r/}0jE/)s)cjW#w'Sʯ5<66lj$a~3Kʛy 2:cZ:Yh))+a߭K::N,Q F'qB]={.]h85C9cr=}*rk?vwV렵ٸW Rs%}rNAkDv|uFLBkWY YkX מ|)1!$#3%y?pF<@<Rr0}: }\J [5FRxY<9"SQdE(Q*Qʻ)q1E0B_O24[U'],lOb ]~WjHޏTQ5Syu wq)xnw8~)c 쫬gٲߠ H% k5dƝk> kEj,0% b"vi2Wس_CuK)K{n|>t{P1򨾜j>'kEkƗBg*H%'_aY6Bn!TL&ɌOb{c`'d^{t\i^[uɐ[}q0lM˕G:‚4kb祔c^:?bpg… +37stH:0}en6x˟%/<]BL&* 5&fK9Mq)/iyqtA%kUe[ڛKN]Ě^,"`/ s[EQQm?|XJ߅92m]G.E΃ח U*Cn.j_)Tѧj̿30ڇ!A0=͜ar I3$C^-9#|pk!)?7.x9 @OO;WƝZBFU keZ75F6Tc6"ZȚs2y/1 ʵ:u4xa`C>6Rb/Yм)^=+~uRd`/|_8xbB0?Ft||Z\##|K 0>>zxv8۴吅q 8ĥ)"6>~\8:qM}#͚'ĉ#p\׶ l#bA?)|g g9|8jP(cr,BwV (WliVxxᡁ@0Okn;ɥh$_ckCgriv}>=wGzβ KkBɛ[˪ !J)h&k2%07δt}!d<9;I&0wV/ v 0<H}L&8ob%Hi|޶o&h1L|u֦y~󛱢8fٲUsւ)0oiFx2}X[zVYr_;N(w]_4B@OanC?gĦx>мgx>ΛToZoOMp>40>V Oy V9iq!4 LN,ˢu{jsz]|"R޻&'ƚ{53ўFu(<٪9:΋]B;)B>1::8;~)Yt|0(pw2N%&X,URBK)3\zz&}ax4;ǟ(tLNg{N|Ǽ\G#C9g$^\}p?556]/RP.90 k,U8/u776s ʪ_01چ|\N 0VV*3H鴃J7iI!wG_^ypl}r*jɤSR 5QN@ iZ#1ٰy;_\3\BQQ x:WJv츟ٯ$"@6 S#qe딇(/P( Dy~TOϻ<4:-+F`0||;Xl-"uw$Цi󼕝mKʩorz"mϺ$F:~E'ҐvD\y?Rr8_He@ e~O,T.(ފR*cY^m|cVR[8 JҡSm!ΆԨb)RHG{?MpqrmN>߶Y)\p,d#xۆWY*,l6]v0h15M˙MS8+EdI='LBJIH7_9{Caз*Lq,dt >+~ّeʏ?xԕ4bBAŚjﵫ!'\Ը$WNvKO}ӽmSşذqsOy?\[,d@'73'j%kOe`1.g2"e =YIzS2|zŐƄa\U,dP;jhhhaxǶ?КZ՚.q SE+XrbOu%\GتX(H,N^~]JyEZQKceTQ]VGYqnah;y$cQahT&QPZ*iZ8UQQM.qo/T\7X"u?Mttl2Xq(IoW{R^ ux*SYJ! 4S.Jy~ BROS[V|žKNɛP(L6V^|cR7i7nZW1Fd@ Ara{詑|(T*dN]Ko?s=@ |_EvF]׍kR)eBJc" MUUbY6`~V޴dJKß&~'d3i WWWWWW
Current Directory: /opt/saltstack/salt/lib/python3.10/site-packages/salt/modules
Viewing File: /opt/saltstack/salt/lib/python3.10/site-packages/salt/modules/rpmbuild_pkgbuild.py
""" RPM Package builder system .. versionadded:: 2015.8.0 This system allows for all of the components to build rpms safely in chrooted environments. This also provides a function to generate yum repositories This module implements the pkgbuild interface """ import errno import functools import logging import os import re import shutil import tempfile import time import traceback import urllib.parse import salt.utils.files import salt.utils.path import salt.utils.user import salt.utils.vt from salt.exceptions import CommandExecutionError, SaltInvocationError HAS_LIBS = False try: import gnupg # pylint: disable=unused-import import salt.modules.gpg HAS_LIBS = True except ImportError: pass log = logging.getLogger(__name__) __virtualname__ = "pkgbuild" def __virtual__(): """ Confirm this module is on a RPM based system, and has required utilities """ missing_util = False utils_reqd = ["gpg", "rpm", "rpmbuild", "mock", "createrepo"] for named_util in utils_reqd: if not salt.utils.path.which(named_util): missing_util = True break if HAS_LIBS and not missing_util: if __grains__.get("os_family", False) in ("RedHat", "Suse"): return __virtualname__ else: # The module will be exposed as `rpmbuild` on non-RPM based systems return "rpmbuild" else: return ( False, "The rpmbuild module could not be loaded: requires python-gnupg, " "gpg, rpm, rpmbuild, mock and createrepo utilities to be installed", ) def _create_rpmmacros(runas="root"): """ Create the .rpmmacros file in user's home directory """ home = os.path.expanduser("~" + runas) rpmbuilddir = os.path.join(home, "rpmbuild") if not os.path.isdir(rpmbuilddir): __salt__["file.makedirs_perms"](name=rpmbuilddir, user=runas, group="mock") mockdir = os.path.join(home, "mock") if not os.path.isdir(mockdir): __salt__["file.makedirs_perms"](name=mockdir, user=runas, group="mock") rpmmacros = os.path.join(home, ".rpmmacros") with salt.utils.files.fopen(rpmmacros, "w") as afile: afile.write(salt.utils.stringutils.to_str(f"%_topdir {rpmbuilddir}\n")) afile.write("%signature gpg\n") afile.write("%_source_filedigest_algorithm 8\n") afile.write("%_binary_filedigest_algorithm 8\n") afile.write("%_gpg_name packaging@saltstack.com\n") def _mk_tree(runas="root"): """ Create the rpm build tree """ basedir = tempfile.mkdtemp() paths = ["BUILD", "RPMS", "SOURCES", "SPECS", "SRPMS"] for path in paths: full = os.path.join(basedir, path) __salt__["file.makedirs_perms"](name=full, user=runas, group="mock") return basedir def _get_spec(tree_base, spec, template, saltenv="base"): """ Get the spec file and place it in the SPECS dir """ spec_tgt = os.path.basename(spec) dest = os.path.join(tree_base, "SPECS", spec_tgt) return __salt__["cp.get_url"](spec, dest, saltenv=saltenv) def _get_src(tree_base, source, saltenv="base", runas="root"): """ Get the named sources and place them into the tree_base """ parsed = urllib.parse.urlparse(source) sbase = os.path.basename(source) dest = os.path.join(tree_base, "SOURCES", sbase) if parsed.scheme: lsrc = __salt__["cp.get_url"](source, dest, saltenv=saltenv) else: shutil.copy(source, dest) __salt__["file.chown"](path=dest, user=runas, group="mock") def _get_distset(tgt): """ Get the distribution string for use with rpmbuild and mock """ # Centos adds 'centos' string to rpm names, removing that to have # consistent naming on Centos and Redhat, and allow for Amazon naming tgtattrs = tgt.split("-") if tgtattrs[0] == "amzn2": distset = f'--define "dist .{tgtattrs[0]}"' elif tgtattrs[1] in ["6", "7", "8"]: distset = f'--define "dist .el{tgtattrs[1]}"' else: distset = "" return distset def _get_deps(deps, tree_base, saltenv="base"): """ Get include string for list of dependent rpms to build package """ deps_list = "" if deps is None: return deps_list if not isinstance(deps, list): raise SaltInvocationError( "'deps' must be a Python list or comma-separated string" ) for deprpm in deps: parsed = urllib.parse.urlparse(deprpm) depbase = os.path.basename(deprpm) dest = os.path.join(tree_base, depbase) if parsed.scheme: __salt__["cp.get_url"](deprpm, dest, saltenv=saltenv) else: shutil.copy(deprpm, dest) deps_list += f" {dest}" return deps_list def _check_repo_gpg_phrase_utils(): """ Check for /usr/libexec/gpg-preset-passphrase is installed """ util_name = "/usr/libexec/gpg-preset-passphrase" if __salt__["file.file_exists"](util_name): return True else: raise CommandExecutionError(f"utility '{util_name}' needs to be installed") def _get_gpg_key_resources(keyid, env, use_passphrase, gnupghome, runas): """ Obtain gpg key resource infomation to sign repo files with keyid Optional Key ID to use in signing packages and repository. Utilizes Public and Private keys associated with keyid which have been loaded into the minion's Pillar data. env A dictionary of environment variables to be utilized in creating the repository. use_passphrase : False Use a passphrase with the signing key presented in ``keyid``. Passphrase is received from Pillar data which could be passed on the command line with ``pillar`` parameter. gnupghome : /etc/salt/gpgkeys Location where GPG related files are stored, used with ``keyid``. runas : root User to create the repository as, and optionally sign packages. .. note:: Ensure the user has correct permissions to any files and directories which are to be utilized. Returns: tuple use_gpg_agent True | False, Redhat 8 now makes use of a gpg-agent similar ot Debian local_keyid key id to use in signing define_gpg_name string containing definition to use with addsign (use_gpg_agent False) phrase pass phrase (may not be used) """ local_keygrip_to_use = None local_key_fingerprint = None local_keyid = None local_uids = None define_gpg_name = "" phrase = "" retrc = 0 use_gpg_agent = False if ( __grains__.get("os_family") == "RedHat" and __grains__.get("osmajorrelease") >= 8 ): use_gpg_agent = True if keyid is not None: # import_keys pkg_pub_key_file = "{}/{}".format( gnupghome, __salt__["pillar.get"]("gpg_pkg_pub_keyname", None) ) pkg_priv_key_file = "{}/{}".format( gnupghome, __salt__["pillar.get"]("gpg_pkg_priv_keyname", None) ) if pkg_pub_key_file is None or pkg_priv_key_file is None: raise SaltInvocationError( "Pillar data should contain Public and Private keys associated with" " 'keyid'" ) try: __salt__["gpg.import_key"]( user=runas, filename=pkg_pub_key_file, gnupghome=gnupghome ) __salt__["gpg.import_key"]( user=runas, filename=pkg_priv_key_file, gnupghome=gnupghome ) except SaltInvocationError: raise SaltInvocationError( "Public and Private key files associated with Pillar data and 'keyid' " "{} could not be found".format(keyid) ) # gpg keys should have been loaded as part of setup # retrieve specified key and preset passphrase local_keys = __salt__["gpg.list_keys"](user=runas, gnupghome=gnupghome) for gpg_key in local_keys: if keyid == gpg_key["keyid"][8:]: local_uids = gpg_key["uids"] local_keyid = gpg_key["keyid"] if use_gpg_agent: local_keygrip_to_use = gpg_key["fingerprint"] local_key_fingerprint = gpg_key["fingerprint"] break if use_gpg_agent: cmd = "gpg --with-keygrip --list-secret-keys" local_keys2_keygrip = __salt__["cmd.run"](cmd, runas=runas, env=env) local_keys2 = iter(local_keys2_keygrip.splitlines()) try: for line in local_keys2: if line.startswith("sec"): line_fingerprint = next(local_keys2).lstrip().rstrip() if local_key_fingerprint == line_fingerprint: lkeygrip = next(local_keys2).split("=") local_keygrip_to_use = lkeygrip[1].lstrip().rstrip() break except StopIteration: raise SaltInvocationError( "unable to find keygrip associated with fingerprint '{}' for keyid" " '{}'".format(local_key_fingerprint, local_keyid) ) if local_keyid is None: raise SaltInvocationError( "The key ID '{}' was not found in GnuPG keyring at '{}'".format( keyid, gnupghome ) ) if use_passphrase: phrase = __salt__["pillar.get"]("gpg_passphrase") if use_gpg_agent: _check_repo_gpg_phrase_utils() cmd = ( "/usr/libexec/gpg-preset-passphrase --verbose --preset " '--passphrase "{}" {}'.format(phrase, local_keygrip_to_use) ) retrc = __salt__["cmd.retcode"](cmd, runas=runas, env=env) if retrc != 0: raise SaltInvocationError( "Failed to preset passphrase, error {1}, " "check logs for further details".format(retrc) ) if local_uids: define_gpg_name = ( "--define='%_signature gpg' --define='%_gpg_name {}'".format( local_uids[0] ) ) # need to update rpm with public key cmd = f"rpm --import {pkg_pub_key_file}" retrc = __salt__["cmd.retcode"](cmd, runas=runas, use_vt=True) if retrc != 0: raise SaltInvocationError( "Failed to import public key from file {} with return " "error {}, check logs for further details".format( pkg_pub_key_file, retrc ) ) return (use_gpg_agent, local_keyid, define_gpg_name, phrase) def _sign_file(runas, define_gpg_name, phrase, abs_file, timeout): """ Sign file with provided key and definition """ SIGN_PROMPT_RE = re.compile(r"Enter pass phrase: ", re.M) # interval of 0.125 is really too fast on some systems interval = 0.5 number_retries = timeout / interval times_looped = 0 error_msg = f"Failed to sign file {abs_file}" cmd = f"rpm {define_gpg_name} --addsign {abs_file}" preexec_fn = functools.partial(salt.utils.user.chugid_and_umask, runas, None) try: stdout, stderr = None, None proc = salt.utils.vt.Terminal( cmd, shell=True, preexec_fn=preexec_fn, stream_stdout=True, stream_stderr=True, ) while proc.has_unread_data: stdout, stderr = proc.recv() if stdout and SIGN_PROMPT_RE.search(stdout): # have the prompt for inputting the passphrase proc.sendline(phrase) else: times_looped += 1 if times_looped > number_retries: raise SaltInvocationError( "Attemping to sign file {} failed, timed out after {} seconds".format( abs_file, int(times_looped * interval) ) ) time.sleep(interval) proc_exitstatus = proc.exitstatus if proc_exitstatus != 0: raise SaltInvocationError( "Signing file {} failed with proc.status {}".format( abs_file, proc_exitstatus ) ) except salt.utils.vt.TerminalException as err: trace = traceback.format_exc() log.error(error_msg, err, trace) finally: proc.close(terminate=True, kill=True) def _sign_files_with_gpg_agent(runas, local_keyid, abs_file, repodir, env, timeout): """ Sign file with provided key utilizing gpg-agent """ cmd = f"rpmsign --verbose --key-id={local_keyid} --addsign {abs_file}" retrc = __salt__["cmd.retcode"](cmd, runas=runas, cwd=repodir, use_vt=True, env=env) if retrc != 0: raise SaltInvocationError( "Signing encountered errors for command '{}', " "return error {}, check logs for further details".format(cmd, retrc) ) def make_src_pkg( dest_dir, spec, sources, env=None, template=None, saltenv="base", runas="root" ): """ Create a source rpm from the given spec file and sources CLI Example: .. code-block:: bash salt '*' pkgbuild.make_src_pkg /var/www/html/ https://raw.githubusercontent.com/saltstack/libnacl/master/pkg/rpm/python-libnacl.spec https://pypi.python.org/packages/source/l/libnacl/libnacl-1.3.5.tar.gz This example command should build the libnacl SOURCE package and place it in /var/www/html/ on the minion .. versionchanged:: 2017.7.0 dest_dir The directory on the minion to place the built package(s) spec The location of the spec file (used for rpms) sources The list of package sources env A dictionary of environment variables to be set prior to execution. template Run the spec file through a templating engine Optional argument, allows for no templating engine used to be if none is desired. saltenv The saltenv to use for files downloaded from the salt filesever runas The user to run the build process as .. versionadded:: 2018.3.3 .. note:: using SHA256 as digest and minimum level dist el6 """ _create_rpmmacros(runas) tree_base = _mk_tree(runas) spec_path = _get_spec(tree_base, spec, template, saltenv) __salt__["file.chown"](path=spec_path, user=runas, group="mock") __salt__["file.chown"](path=tree_base, user=runas, group="mock") if isinstance(sources, str): sources = sources.split(",") for src in sources: _get_src(tree_base, src, saltenv, runas) # make source rpms for dist el6 with SHA256, usable with mock on other dists cmd = 'rpmbuild --verbose --define "_topdir {}" -bs --define "dist .el6" {}'.format( tree_base, spec_path ) retrc = __salt__["cmd.retcode"](cmd, runas=runas) if retrc != 0: raise SaltInvocationError( "Make source package for destination directory {}, spec {}, sources {}," " failed with return error {}, check logs for further details".format( dest_dir, spec, sources, retrc ) ) srpms = os.path.join(tree_base, "SRPMS") ret = [] if not os.path.isdir(dest_dir): __salt__["file.makedirs_perms"](name=dest_dir, user=runas, group="mock") for fn_ in os.listdir(srpms): full = os.path.join(srpms, fn_) tgt = os.path.join(dest_dir, fn_) shutil.copy(full, tgt) ret.append(tgt) return ret def build( runas, tgt, dest_dir, spec, sources, deps, env, template, saltenv="base", log_dir="/var/log/salt/pkgbuild", ): """ Given the package destination directory, the spec file source and package sources, use mock to safely build the rpm defined in the spec file CLI Example: .. code-block:: bash salt '*' pkgbuild.build mock epel-7-x86_64 /var/www/html https://raw.githubusercontent.com/saltstack/libnacl/master/pkg/rpm/python-libnacl.spec https://pypi.python.org/packages/source/l/libnacl/libnacl-1.3.5.tar.gz This example command should build the libnacl package for rhel 7 using user mock and place it in /var/www/html/ on the minion """ ret = {} try: __salt__["file.chown"](path=dest_dir, user=runas, group="mock") except OSError as exc: if exc.errno != errno.EEXIST: raise srpm_dir = os.path.join(dest_dir, "SRPMS") srpm_build_dir = tempfile.mkdtemp() try: srpms = make_src_pkg( srpm_build_dir, spec, sources, env, template, saltenv, runas ) except Exception as exc: # pylint: disable=broad-except shutil.rmtree(srpm_build_dir) log.error("Failed to make src package") return ret distset = _get_distset(tgt) noclean = "" deps_dir = tempfile.mkdtemp() deps_list = _get_deps(deps, deps_dir, saltenv) retrc = 0 for srpm in srpms: dbase = os.path.dirname(srpm) results_dir = tempfile.mkdtemp() try: __salt__["file.chown"](path=dbase, user=runas, group="mock") __salt__["file.chown"](path=results_dir, user=runas, group="mock") cmd = f"mock --root={tgt} --resultdir={results_dir} --init" retrc |= __salt__["cmd.retcode"](cmd, runas=runas) if deps_list and not deps_list.isspace(): cmd = "mock --root={} --resultdir={} --install {} {}".format( tgt, results_dir, deps_list, noclean ) retrc |= __salt__["cmd.retcode"](cmd, runas=runas) noclean += " --no-clean" cmd = "mock --root={} --resultdir={} {} {} {}".format( tgt, results_dir, distset, noclean, srpm ) retrc |= __salt__["cmd.retcode"](cmd, runas=runas) cmdlist = [ "rpm", "-qp", "--queryformat", f"{log_dir}/%{{name}}/%{{version}}-%{{release}}", srpm, ] log_dest = __salt__["cmd.run_stdout"](cmdlist, python_shell=False) for filename in os.listdir(results_dir): full = os.path.join(results_dir, filename) if filename.endswith("src.rpm"): sdest = os.path.join(srpm_dir, filename) try: __salt__["file.makedirs_perms"]( name=srpm_dir, user=runas, group="mock" ) except OSError as exc: if exc.errno != errno.EEXIST: raise shutil.copy(full, sdest) ret.setdefault("Source Packages", []).append(sdest) elif filename.endswith(".rpm"): bdist = os.path.join(dest_dir, filename) shutil.copy(full, bdist) ret.setdefault("Packages", []).append(bdist) else: log_file = os.path.join(log_dest, filename) try: __salt__["file.makedirs_perms"]( name=log_dest, user=runas, group="mock" ) except OSError as exc: if exc.errno != errno.EEXIST: raise shutil.copy(full, log_file) ret.setdefault("Log Files", []).append(log_file) except Exception as exc: # pylint: disable=broad-except log.error("Error building from %s: %s", srpm, exc) finally: shutil.rmtree(results_dir) if retrc != 0: raise SaltInvocationError( "Building packages for destination directory {}, spec {}, sources {}," " failed with return error {}, check logs for further details".format( dest_dir, spec, sources, retrc ) ) shutil.rmtree(deps_dir) shutil.rmtree(srpm_build_dir) return ret def make_repo( repodir, keyid=None, env=None, use_passphrase=False, gnupghome="/etc/salt/gpgkeys", runas="root", timeout=15.0, ): """ Make a package repository and optionally sign packages present Given the repodir, create a ``yum`` repository out of the rpms therein and optionally sign it and packages present, the name is directory to turn into a repo. This state is best used with onchanges linked to your package building states. repodir The directory to find packages that will be in the repository. keyid .. versionchanged:: 2016.3.0 Optional Key ID to use in signing packages and repository. Utilizes Public and Private keys associated with keyid which have been loaded into the minion's Pillar data. For example, contents from a Pillar data file with named Public and Private keys as follows: .. code-block:: yaml gpg_pkg_priv_key: | -----BEGIN PGP PRIVATE KEY BLOCK----- Version: GnuPG v1 lQO+BFciIfQBCADAPCtzx7I5Rl32escCMZsPzaEKWe7bIX1em4KCKkBoX47IG54b w82PCE8Y1jF/9Uk2m3RKVWp3YcLlc7Ap3gj6VO4ysvVz28UbnhPxsIkOlf2cq8qc . . Ebe+8JCQTwqSXPRTzXmy/b5WXDeM79CkLWvuGpXFor76D+ECMRPv/rawukEcNptn R5OmgHqvydEnO4pWbn8JzQO9YX/Us0SMHBVzLC8eIi5ZIopzalvX =JvW8 -----END PGP PRIVATE KEY BLOCK----- gpg_pkg_priv_keyname: gpg_pkg_key.pem gpg_pkg_pub_key: | -----BEGIN PGP PUBLIC KEY BLOCK----- Version: GnuPG v1 mQENBFciIfQBCADAPCtzx7I5Rl32escCMZsPzaEKWe7bIX1em4KCKkBoX47IG54b w82PCE8Y1jF/9Uk2m3RKVWp3YcLlc7Ap3gj6VO4ysvVz28UbnhPxsIkOlf2cq8qc . . bYP7t5iwJmQzRMyFInYRt77wkJBPCpJc9FPNebL9vlZcN4zv0KQta+4alcWivvoP 4QIxE+/+trC6QRw2m2dHk6aAeq/J0Sc7ilZufwnNA71hf9SzRIwcFXMsLx4iLlki inNqW9c= =s1CX -----END PGP PUBLIC KEY BLOCK----- gpg_pkg_pub_keyname: gpg_pkg_key.pub env .. versionchanged:: 2016.3.0 A dictionary of environment variables to be utilized in creating the repository. .. note:: This parameter is not used for making ``yum`` repositories. use_passphrase : False .. versionadded:: 2016.3.0 Use a passphrase with the signing key presented in ``keyid``. Passphrase is received from Pillar data which could be passed on the command line with ``pillar`` parameter. .. code-block:: bash pillar='{ "gpg_passphrase" : "my_passphrase" }' .. versionadded:: 3001.1 RHEL 8 and above leverages gpg-agent and gpg-preset-passphrase for caching keys, etc. gnupghome : /etc/salt/gpgkeys .. versionadded:: 2016.3.0 Location where GPG related files are stored, used with ``keyid``. runas : root .. versionadded:: 2016.3.0 User to create the repository as, and optionally sign packages. .. note:: Ensure the user has correct permissions to any files and directories which are to be utilized. timeout : 15.0 .. versionadded:: 2016.3.4 Timeout in seconds to wait for the prompt for inputting the passphrase. CLI Example: .. code-block:: bash salt '*' pkgbuild.make_repo /var/www/html/ """ home = os.path.expanduser("~" + runas) rpmmacros = os.path.join(home, ".rpmmacros") if not os.path.exists(rpmmacros): _create_rpmmacros(runas) if gnupghome and env is None: env = {} env["GNUPGHOME"] = gnupghome use_gpg_agent, local_keyid, define_gpg_name, phrase = _get_gpg_key_resources( keyid, env, use_passphrase, gnupghome, runas ) # sign_it_here for fileused in os.listdir(repodir): if fileused.endswith(".rpm"): abs_file = os.path.join(repodir, fileused) if use_gpg_agent: _sign_files_with_gpg_agent( runas, local_keyid, abs_file, repodir, env, timeout ) else: _sign_file(runas, define_gpg_name, phrase, abs_file, timeout) cmd = f"createrepo --update {repodir}" retrc = __salt__["cmd.run_all"](cmd, runas=runas) return retrc