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/artifactory.py
""" Module for fetching artifacts from Artifactory """ import http.client import logging import os import urllib.request import xml.etree.ElementTree as ET from urllib.error import HTTPError, URLError import salt.utils.files import salt.utils.hashutils import salt.utils.stringutils from salt.exceptions import CommandExecutionError log = logging.getLogger(__name__) __virtualname__ = "artifactory" def __virtual__(): """ Only load if elementtree xml library is available. """ return True def get_latest_snapshot( artifactory_url, repository, group_id, artifact_id, packaging, target_dir="/tmp", target_file=None, classifier=None, username=None, password=None, use_literal_group_id=False, ): """ Gets latest snapshot of the given artifact artifactory_url URL of artifactory instance repository Snapshot repository in artifactory to retrieve artifact from, for example: libs-snapshots group_id Group Id of the artifact artifact_id Artifact Id of the artifact packaging Packaging type (jar,war,ear,etc) target_dir Target directory to download artifact to (default: /tmp) target_file Target file to download artifact to (by default it is target_dir/artifact_id-snapshot_version.packaging) classifier Artifact classifier name (ex: sources,javadoc,etc). Optional parameter. username Artifactory username. Optional parameter. password Artifactory password. Optional parameter. """ log.debug( "======================== MODULE FUNCTION: artifactory.get_latest_snapshot," " artifactory_url=%s, repository=%s, group_id=%s, artifact_id=%s, packaging=%s," " target_dir=%s, classifier=%s)", artifactory_url, repository, group_id, artifact_id, packaging, target_dir, classifier, ) headers = {} if username and password: headers["Authorization"] = "Basic {}".format( salt.utils.hashutils.base64_b64encode( "{}:{}".format(username.replace("\n", ""), password.replace("\n", "")) ) ) artifact_metadata = _get_artifact_metadata( artifactory_url=artifactory_url, repository=repository, group_id=group_id, artifact_id=artifact_id, headers=headers, use_literal_group_id=use_literal_group_id, ) version = artifact_metadata["latest_version"] snapshot_url, file_name = _get_snapshot_url( artifactory_url=artifactory_url, repository=repository, group_id=group_id, artifact_id=artifact_id, version=version, packaging=packaging, classifier=classifier, headers=headers, use_literal_group_id=use_literal_group_id, ) target_file = __resolve_target_file(file_name, target_dir, target_file) return __save_artifact(snapshot_url, target_file, headers) def get_snapshot( artifactory_url, repository, group_id, artifact_id, packaging, version, snapshot_version=None, target_dir="/tmp", target_file=None, classifier=None, username=None, password=None, use_literal_group_id=False, ): """ Gets snapshot of the desired version of the artifact artifactory_url URL of artifactory instance repository Snapshot repository in artifactory to retrieve artifact from, for example: libs-snapshots group_id Group Id of the artifact artifact_id Artifact Id of the artifact packaging Packaging type (jar,war,ear,etc) version Version of the artifact target_dir Target directory to download artifact to (default: /tmp) target_file Target file to download artifact to (by default it is target_dir/artifact_id-snapshot_version.packaging) classifier Artifact classifier name (ex: sources,javadoc,etc). Optional parameter. username Artifactory username. Optional parameter. password Artifactory password. Optional parameter. """ log.debug( "======================== MODULE FUNCTION:" " artifactory.get_snapshot(artifactory_url=%s, repository=%s, group_id=%s," " artifact_id=%s, packaging=%s, version=%s, target_dir=%s, classifier=%s)", artifactory_url, repository, group_id, artifact_id, packaging, version, target_dir, classifier, ) headers = {} if username and password: headers["Authorization"] = "Basic {}".format( salt.utils.hashutils.base64_b64encode( "{}:{}".format(username.replace("\n", ""), password.replace("\n", "")) ) ) snapshot_url, file_name = _get_snapshot_url( artifactory_url=artifactory_url, repository=repository, group_id=group_id, artifact_id=artifact_id, version=version, packaging=packaging, snapshot_version=snapshot_version, classifier=classifier, headers=headers, use_literal_group_id=use_literal_group_id, ) target_file = __resolve_target_file(file_name, target_dir, target_file) return __save_artifact(snapshot_url, target_file, headers) def get_latest_release( artifactory_url, repository, group_id, artifact_id, packaging, target_dir="/tmp", target_file=None, classifier=None, username=None, password=None, use_literal_group_id=False, ): """ Gets the latest release of the artifact artifactory_url URL of artifactory instance repository Release repository in artifactory to retrieve artifact from, for example: libs-releases group_id Group Id of the artifact artifact_id Artifact Id of the artifact packaging Packaging type (jar,war,ear,etc) target_dir Target directory to download artifact to (default: /tmp) target_file Target file to download artifact to (by default it is target_dir/artifact_id-version.packaging) classifier Artifact classifier name (ex: sources,javadoc,etc). Optional parameter. username Artifactory username. Optional parameter. password Artifactory password. Optional parameter. """ log.debug( "======================== MODULE FUNCTION:" " artifactory.get_latest_release(artifactory_url=%s, repository=%s," " group_id=%s, artifact_id=%s, packaging=%s, target_dir=%s, classifier=%s)", artifactory_url, repository, group_id, artifact_id, packaging, target_dir, classifier, ) headers = {} if username and password: headers["Authorization"] = "Basic {}".format( salt.utils.hashutils.base64_b64encode( "{}:{}".format(username.replace("\n", ""), password.replace("\n", "")) ) ) version = __find_latest_version( artifactory_url=artifactory_url, repository=repository, group_id=group_id, artifact_id=artifact_id, headers=headers, ) release_url, file_name = _get_release_url( repository, group_id, artifact_id, packaging, version, artifactory_url, classifier, use_literal_group_id, ) target_file = __resolve_target_file(file_name, target_dir, target_file) return __save_artifact(release_url, target_file, headers) def get_release( artifactory_url, repository, group_id, artifact_id, packaging, version, target_dir="/tmp", target_file=None, classifier=None, username=None, password=None, use_literal_group_id=False, ): """ Gets the specified release of the artifact artifactory_url URL of artifactory instance repository Release repository in artifactory to retrieve artifact from, for example: libs-releases group_id Group Id of the artifact artifact_id Artifact Id of the artifact packaging Packaging type (jar,war,ear,etc) version Version of the artifact target_dir Target directory to download artifact to (default: /tmp) target_file Target file to download artifact to (by default it is target_dir/artifact_id-version.packaging) classifier Artifact classifier name (ex: sources,javadoc,etc). Optional parameter. username Artifactory username. Optional parameter. password Artifactory password. Optional parameter. """ log.debug( "======================== MODULE FUNCTION:" " artifactory.get_release(artifactory_url=%s, repository=%s, group_id=%s," " artifact_id=%s, packaging=%s, version=%s, target_dir=%s, classifier=%s)", artifactory_url, repository, group_id, artifact_id, packaging, version, target_dir, classifier, ) headers = {} if username and password: headers["Authorization"] = "Basic {}".format( salt.utils.hashutils.base64_b64encode( "{}:{}".format(username.replace("\n", ""), password.replace("\n", "")) ## f"{username.replace("\n", "")}:{password.replace("\n", "")}") ) ) release_url, file_name = _get_release_url( repository, group_id, artifact_id, packaging, version, artifactory_url, classifier, use_literal_group_id, ) target_file = __resolve_target_file(file_name, target_dir, target_file) return __save_artifact(release_url, target_file, headers) def __resolve_target_file(file_name, target_dir, target_file=None): if target_file is None: target_file = os.path.join(target_dir, file_name) return target_file def _get_snapshot_url( artifactory_url, repository, group_id, artifact_id, version, packaging, snapshot_version=None, classifier=None, headers=None, use_literal_group_id=False, ): if headers is None: headers = {} has_classifier = classifier is not None and classifier != "" if snapshot_version is None: try: snapshot_version_metadata = _get_snapshot_version_metadata( artifactory_url=artifactory_url, repository=repository, group_id=group_id, artifact_id=artifact_id, version=version, headers=headers, ) if ( not has_classifier and packaging not in snapshot_version_metadata["snapshot_versions"] ): error_message = """Cannot find requested packaging '{packaging}' in the snapshot version metadata. artifactory_url: {artifactory_url} repository: {repository} group_id: {group_id} artifact_id: {artifact_id} packaging: {packaging} classifier: {classifier} version: {version}""".format( artifactory_url=artifactory_url, repository=repository, group_id=group_id, artifact_id=artifact_id, packaging=packaging, classifier=classifier, version=version, ) raise ArtifactoryError(error_message) packaging_with_classifier = ( packaging if not has_classifier else packaging + ":" + classifier ) if ( has_classifier and packaging_with_classifier not in snapshot_version_metadata["snapshot_versions"] ): error_message = """Cannot find requested classifier '{classifier}' in the snapshot version metadata. artifactory_url: {artifactory_url} repository: {repository} group_id: {group_id} artifact_id: {artifact_id} packaging: {packaging} classifier: {classifier} version: {version}""".format( artifactory_url=artifactory_url, repository=repository, group_id=group_id, artifact_id=artifact_id, packaging=packaging, classifier=classifier, version=version, ) raise ArtifactoryError(error_message) snapshot_version = snapshot_version_metadata["snapshot_versions"][ packaging_with_classifier ] except CommandExecutionError as err: log.error( "Could not fetch maven-metadata.xml. Assuming snapshot_version=%s.", version, ) snapshot_version = version group_url = __get_group_id_subpath(group_id, use_literal_group_id) file_name = "{artifact_id}-{snapshot_version}{classifier}.{packaging}".format( artifact_id=artifact_id, snapshot_version=snapshot_version, packaging=packaging, classifier=__get_classifier_url(classifier), ) snapshot_url = "{artifactory_url}/{repository}/{group_url}/{artifact_id}/{version}/{file_name}".format( artifactory_url=artifactory_url, repository=repository, group_url=group_url, artifact_id=artifact_id, version=version, file_name=file_name, ) log.debug("snapshot_url=%s", snapshot_url) return snapshot_url, file_name def _get_release_url( repository, group_id, artifact_id, packaging, version, artifactory_url, classifier=None, use_literal_group_id=False, ): group_url = __get_group_id_subpath(group_id, use_literal_group_id) # for released versions the suffix for the file is same as version file_name = "{artifact_id}-{version}{classifier}.{packaging}".format( artifact_id=artifact_id, version=version, packaging=packaging, classifier=__get_classifier_url(classifier), ) release_url = "{artifactory_url}/{repository}/{group_url}/{artifact_id}/{version}/{file_name}".format( artifactory_url=artifactory_url, repository=repository, group_url=group_url, artifact_id=artifact_id, version=version, file_name=file_name, ) log.debug("release_url=%s", release_url) return release_url, file_name def _get_artifact_metadata_url( artifactory_url, repository, group_id, artifact_id, use_literal_group_id=False ): group_url = __get_group_id_subpath(group_id, use_literal_group_id) # for released versions the suffix for the file is same as version artifact_metadata_url = "{artifactory_url}/{repository}/{group_url}/{artifact_id}/maven-metadata.xml".format( artifactory_url=artifactory_url, repository=repository, group_url=group_url, artifact_id=artifact_id, ) log.debug("artifact_metadata_url=%s", artifact_metadata_url) return artifact_metadata_url def _get_artifact_metadata_xml( artifactory_url, repository, group_id, artifact_id, headers, use_literal_group_id=False, ): artifact_metadata_url = _get_artifact_metadata_url( artifactory_url=artifactory_url, repository=repository, group_id=group_id, artifact_id=artifact_id, use_literal_group_id=use_literal_group_id, ) try: request = urllib.request.Request(artifact_metadata_url, None, headers) artifact_metadata_xml = urllib.request.urlopen(request).read() except (HTTPError, URLError) as err: message = "Could not fetch data from url: {}. ERROR: {}".format( artifact_metadata_url, err ) raise CommandExecutionError(message) log.debug("artifact_metadata_xml=%s", artifact_metadata_xml) return artifact_metadata_xml def _get_artifact_metadata( artifactory_url, repository, group_id, artifact_id, headers, use_literal_group_id=False, ): metadata_xml = _get_artifact_metadata_xml( artifactory_url=artifactory_url, repository=repository, group_id=group_id, artifact_id=artifact_id, headers=headers, use_literal_group_id=use_literal_group_id, ) root = ET.fromstring(metadata_xml) assert group_id == root.find("groupId").text assert artifact_id == root.find("artifactId").text latest_version = root.find("versioning").find("latest").text return {"latest_version": latest_version} # functions for handling snapshots def _get_snapshot_version_metadata_url( artifactory_url, repository, group_id, artifact_id, version, use_literal_group_id=False, ): group_url = __get_group_id_subpath(group_id, use_literal_group_id) # for released versions the suffix for the file is same as version snapshot_version_metadata_url = "{artifactory_url}/{repository}/{group_url}/{artifact_id}/{version}/maven-metadata.xml".format( artifactory_url=artifactory_url, repository=repository, group_url=group_url, artifact_id=artifact_id, version=version, ) log.debug("snapshot_version_metadata_url=%s", snapshot_version_metadata_url) return snapshot_version_metadata_url def _get_snapshot_version_metadata_xml( artifactory_url, repository, group_id, artifact_id, version, headers, use_literal_group_id=False, ): snapshot_version_metadata_url = _get_snapshot_version_metadata_url( artifactory_url=artifactory_url, repository=repository, group_id=group_id, artifact_id=artifact_id, version=version, use_literal_group_id=use_literal_group_id, ) try: request = urllib.request.Request(snapshot_version_metadata_url, None, headers) snapshot_version_metadata_xml = urllib.request.urlopen(request).read() except (HTTPError, URLError) as err: message = "Could not fetch data from url: {}. ERROR: {}".format( snapshot_version_metadata_url, err ) raise CommandExecutionError(message) log.debug("snapshot_version_metadata_xml=%s", snapshot_version_metadata_xml) return snapshot_version_metadata_xml def _get_snapshot_version_metadata( artifactory_url, repository, group_id, artifact_id, version, headers ): metadata_xml = _get_snapshot_version_metadata_xml( artifactory_url=artifactory_url, repository=repository, group_id=group_id, artifact_id=artifact_id, version=version, headers=headers, ) metadata = ET.fromstring(metadata_xml) assert group_id == metadata.find("groupId").text assert artifact_id == metadata.find("artifactId").text assert version == metadata.find("version").text snapshot_versions = metadata.find("versioning").find("snapshotVersions") extension_version_dict = {} for snapshot_version in snapshot_versions: extension = snapshot_version.find("extension").text value = snapshot_version.find("value").text if snapshot_version.find("classifier") is not None: classifier = snapshot_version.find("classifier").text extension_version_dict[extension + ":" + classifier] = value else: extension_version_dict[extension] = value return {"snapshot_versions": extension_version_dict} def __get_latest_version_url( artifactory_url, repository, group_id, artifact_id, use_literal_group_id=False ): group_url = __get_group_id_subpath(group_id, use_literal_group_id) # for released versions the suffix for the file is same as version latest_version_url = "{artifactory_url}/api/search/latestVersion?g={group_url}&a={artifact_id}&repos={repository}".format( artifactory_url=artifactory_url, repository=repository, group_url=group_url, artifact_id=artifact_id, ) log.debug("latest_version_url=%s", latest_version_url) return latest_version_url def __find_latest_version( artifactory_url, repository, group_id, artifact_id, headers, use_literal_group_id=False, ): latest_version_url = __get_latest_version_url( artifactory_url=artifactory_url, repository=repository, group_id=group_id, artifact_id=artifact_id, use_literal_group_id=use_literal_group_id, ) try: request = urllib.request.Request(latest_version_url, None, headers) version = urllib.request.urlopen(request).read() except (HTTPError, URLError) as err: message = "Could not fetch data from url: {}. ERROR: {}".format( latest_version_url, err ) raise CommandExecutionError(message) log.debug("Response of: %s", version) if version is None or version == "": raise ArtifactoryError("Unable to find release version") return version def __save_artifact(artifact_url, target_file, headers): log.debug("__save_artifact(%s, %s)", artifact_url, target_file) result = {"status": False, "changes": {}, "comment": ""} if os.path.isfile(target_file): log.debug("File %s already exists, checking checksum...", target_file) checksum_url = artifact_url + ".sha1" checksum_success, artifact_sum, checksum_comment = __download( checksum_url, headers ) if checksum_success: artifact_sum = salt.utils.stringutils.to_unicode(artifact_sum) log.debug("Downloaded SHA1 SUM: %s", artifact_sum) file_sum = __salt__["file.get_hash"](path=target_file, form="sha1") log.debug("Target file (%s) SHA1 SUM: %s", target_file, file_sum) if artifact_sum == file_sum: result["status"] = True result["target_file"] = target_file result["comment"] = ( "File {} already exists, checksum matches with Artifactory.\n" "Checksum URL: {}".format(target_file, checksum_url) ) return result else: result["comment"] = ( "File {} already exists, checksum does not match with" " Artifactory!\nChecksum URL: {}".format(target_file, checksum_url) ) else: result["status"] = False result["comment"] = checksum_comment return result log.debug("Downloading: %s -> %s", artifact_url, target_file) try: request = urllib.request.Request(artifact_url, None, headers) f = urllib.request.urlopen(request) with salt.utils.files.fopen(target_file, "wb") as local_file: local_file.write(salt.utils.stringutils.to_bytes(f.read())) result["status"] = True result["comment"] = __append_comment( f"Artifact downloaded from URL: {artifact_url}", result["comment"], ) result["changes"]["downloaded_file"] = target_file result["target_file"] = target_file except (HTTPError, URLError) as e: result["status"] = False result["comment"] = __get_error_comment(e, artifact_url) return result def __get_group_id_subpath(group_id, use_literal_group_id=False): if not use_literal_group_id: group_url = group_id.replace(".", "/") return group_url return group_id def __get_classifier_url(classifier): has_classifier = classifier is not None and classifier != "" return "-" + classifier if has_classifier else "" def __download(request_url, headers): log.debug("Downloading content from %s", request_url) success = False content = None comment = None try: request = urllib.request.Request(request_url, None, headers) url = urllib.request.urlopen(request) content = url.read() success = True except HTTPError as e: comment = __get_error_comment(e, request_url) return success, content, comment def __get_error_comment(http_error, request_url): if http_error.code == http.client.NOT_FOUND: comment = "HTTP Error 404. Request URL: " + request_url elif http_error.code == http.client.CONFLICT: comment = ( "HTTP Error 409: Conflict. Requested URL: {}. \nThis error may be caused by" " reading snapshot artifact from non-snapshot repository.".format( request_url ) ) else: comment = "HTTP Error {err_code}. Request URL: {url}".format( err_code=http_error.code, url=request_url ) return comment def __append_comment(new_comment, current_comment=""): return current_comment + "\n" + new_comment class ArtifactoryError(Exception): def __init__(self, value): super().__init__() self.value = value def __str__(self): return repr(self.value)