test_file.py 183 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754
  1. # -*- coding: utf-8 -*-
  2. '''
  3. Tests for the file state
  4. '''
  5. # Import Python libs
  6. from __future__ import absolute_import, print_function, unicode_literals
  7. import errno
  8. import logging
  9. import os
  10. import re
  11. import sys
  12. import shutil
  13. import stat
  14. import tempfile
  15. import textwrap
  16. import filecmp
  17. log = logging.getLogger(__name__)
  18. # Import Salt Testing libs
  19. from tests.support.case import ModuleCase
  20. from tests.support.unit import skipIf
  21. from tests.support.paths import BASE_FILES, FILES, TMP, TMP_STATE_TREE
  22. from tests.support.helpers import (
  23. destructiveTest,
  24. skip_if_not_root,
  25. with_system_user_and_group,
  26. with_tempdir,
  27. with_tempfile,
  28. Webserver,
  29. destructiveTest,
  30. dedent,
  31. )
  32. from tests.support.mixins import SaltReturnAssertsMixin
  33. # Import Salt libs
  34. import salt.utils.data
  35. import salt.utils.files
  36. import salt.utils.json
  37. import salt.utils.path
  38. import salt.utils.platform
  39. import salt.utils.stringutils
  40. import salt.serializers.configparser
  41. from salt.utils.versions import LooseVersion as _LooseVersion
  42. HAS_PWD = True
  43. try:
  44. import pwd
  45. except ImportError:
  46. HAS_PWD = False
  47. HAS_GRP = True
  48. try:
  49. import grp
  50. except ImportError:
  51. HAS_GRP = False
  52. # Import 3rd-party libs
  53. from salt.ext import six
  54. from salt.ext.six.moves import range # pylint: disable=import-error,redefined-builtin
  55. IS_WINDOWS = salt.utils.platform.is_windows()
  56. BINARY_FILE = b'GIF89a\x01\x00\x01\x00\x80\x00\x00\x05\x04\x04\x00\x00\x00,\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02D\x01\x00;'
  57. if IS_WINDOWS:
  58. FILEPILLAR = 'C:\\Windows\\Temp\\filepillar-python'
  59. FILEPILLARDEF = 'C:\\Windows\\Temp\\filepillar-defaultvalue'
  60. FILEPILLARGIT = 'C:\\Windows\\Temp\\filepillar-bar'
  61. else:
  62. FILEPILLAR = '/tmp/filepillar-python'
  63. FILEPILLARDEF = '/tmp/filepillar-defaultvalue'
  64. FILEPILLARGIT = '/tmp/filepillar-bar'
  65. TEST_SYSTEM_USER = 'test_system_user'
  66. TEST_SYSTEM_GROUP = 'test_system_group'
  67. def _test_managed_file_mode_keep_helper(testcase, local=False):
  68. '''
  69. DRY helper function to run the same test with a local or remote path
  70. '''
  71. name = os.path.join(TMP, 'scene33')
  72. grail_fs_path = os.path.join(BASE_FILES, 'grail', 'scene33')
  73. grail = 'salt://grail/scene33' if not local else grail_fs_path
  74. # Get the current mode so that we can put the file back the way we
  75. # found it when we're done.
  76. grail_fs_mode = int(testcase.run_function('file.get_mode', [grail_fs_path]), 8)
  77. initial_mode = 0o770
  78. new_mode_1 = 0o600
  79. new_mode_2 = 0o644
  80. # Set the initial mode, so we can be assured that when we set the mode
  81. # to "keep", we're actually changing the permissions of the file to the
  82. # new mode.
  83. ret = testcase.run_state(
  84. 'file.managed',
  85. name=name,
  86. mode=oct(initial_mode),
  87. source=grail,
  88. )
  89. if IS_WINDOWS:
  90. testcase.assertSaltFalseReturn(ret)
  91. return
  92. testcase.assertSaltTrueReturn(ret)
  93. try:
  94. # Update the mode on the fileserver (pass 1)
  95. os.chmod(grail_fs_path, new_mode_1)
  96. ret = testcase.run_state(
  97. 'file.managed',
  98. name=name,
  99. mode='keep',
  100. source=grail,
  101. )
  102. testcase.assertSaltTrueReturn(ret)
  103. managed_mode = stat.S_IMODE(os.stat(name).st_mode)
  104. testcase.assertEqual(oct(managed_mode), oct(new_mode_1))
  105. # Update the mode on the fileserver (pass 2)
  106. # This assures us that if the file in file_roots was originally set
  107. # to the same mode as new_mode_1, we definitely get an updated mode
  108. # this time.
  109. os.chmod(grail_fs_path, new_mode_2)
  110. ret = testcase.run_state(
  111. 'file.managed',
  112. name=name,
  113. mode='keep',
  114. source=grail,
  115. )
  116. testcase.assertSaltTrueReturn(ret)
  117. managed_mode = stat.S_IMODE(os.stat(name).st_mode)
  118. testcase.assertEqual(oct(managed_mode), oct(new_mode_2))
  119. except Exception:
  120. raise
  121. finally:
  122. # Set the mode of the file in the file_roots back to what it
  123. # originally was.
  124. os.chmod(grail_fs_path, grail_fs_mode)
  125. class FileTest(ModuleCase, SaltReturnAssertsMixin):
  126. '''
  127. Validate the file state
  128. '''
  129. def tearDown(self):
  130. '''
  131. remove files created in previous tests
  132. '''
  133. user = 'salt'
  134. if user in str(self.run_function('user.list_users')):
  135. self.run_function('user.delete', [user])
  136. for path in (FILEPILLAR, FILEPILLARDEF, FILEPILLARGIT):
  137. try:
  138. os.remove(path)
  139. except OSError as exc:
  140. if exc.errno != errno.ENOENT:
  141. log.error('Failed to remove %s: %s', path, exc)
  142. def test_symlink(self):
  143. '''
  144. file.symlink
  145. '''
  146. name = os.path.join(TMP, 'symlink')
  147. tgt = os.path.join(TMP, 'target')
  148. # Windows must have a source directory to link to
  149. if IS_WINDOWS and not os.path.isdir(tgt):
  150. os.mkdir(tgt)
  151. # Windows cannot create a symlink if it already exists
  152. if IS_WINDOWS and self.run_function('file.is_link', [name]):
  153. self.run_function('file.remove', [name])
  154. ret = self.run_state('file.symlink', name=name, target=tgt)
  155. self.assertSaltTrueReturn(ret)
  156. def test_test_symlink(self):
  157. '''
  158. file.symlink test interface
  159. '''
  160. name = os.path.join(TMP, 'symlink2')
  161. tgt = os.path.join(TMP, 'target')
  162. ret = self.run_state('file.symlink', test=True, name=name, target=tgt)
  163. self.assertSaltNoneReturn(ret)
  164. def test_absent_file(self):
  165. '''
  166. file.absent
  167. '''
  168. name = os.path.join(TMP, 'file_to_kill')
  169. with salt.utils.files.fopen(name, 'w+') as fp_:
  170. fp_.write('killme')
  171. ret = self.run_state('file.absent', name=name)
  172. self.assertSaltTrueReturn(ret)
  173. self.assertFalse(os.path.isfile(name))
  174. def test_absent_dir(self):
  175. '''
  176. file.absent
  177. '''
  178. name = os.path.join(TMP, 'dir_to_kill')
  179. if not os.path.isdir(name):
  180. # left behind... Don't fail because of this!
  181. os.makedirs(name)
  182. ret = self.run_state('file.absent', name=name)
  183. self.assertSaltTrueReturn(ret)
  184. self.assertFalse(os.path.isdir(name))
  185. def test_absent_link(self):
  186. '''
  187. file.absent
  188. '''
  189. name = os.path.join(TMP, 'link_to_kill')
  190. tgt = '{0}.tgt'.format(name)
  191. # Windows must have a source directory to link to
  192. if IS_WINDOWS and not os.path.isdir(tgt):
  193. os.mkdir(tgt)
  194. if not self.run_function('file.is_link', [name]):
  195. self.run_function('file.symlink', [tgt, name])
  196. ret = self.run_state('file.absent', name=name)
  197. try:
  198. self.assertSaltTrueReturn(ret)
  199. self.assertFalse(self.run_function('file.is_link', [name]))
  200. finally:
  201. if self.run_function('file.is_link', [name]):
  202. self.run_function('file.remove', [name])
  203. @with_tempfile()
  204. def test_test_absent(self, name):
  205. '''
  206. file.absent test interface
  207. '''
  208. with salt.utils.files.fopen(name, 'w+') as fp_:
  209. fp_.write('killme')
  210. ret = self.run_state('file.absent', test=True, name=name)
  211. self.assertSaltNoneReturn(ret)
  212. self.assertTrue(os.path.isfile(name))
  213. def test_managed(self):
  214. '''
  215. file.managed
  216. '''
  217. name = os.path.join(TMP, 'grail_scene33')
  218. ret = self.run_state(
  219. 'file.managed', name=name, source='salt://grail/scene33'
  220. )
  221. src = os.path.join(BASE_FILES, 'grail', 'scene33')
  222. with salt.utils.files.fopen(src, 'r') as fp_:
  223. master_data = fp_.read()
  224. with salt.utils.files.fopen(name, 'r') as fp_:
  225. minion_data = fp_.read()
  226. self.assertEqual(master_data, minion_data)
  227. self.assertSaltTrueReturn(ret)
  228. def test_managed_file_mode(self):
  229. '''
  230. file.managed, correct file permissions
  231. '''
  232. desired_mode = 504 # 0770 octal
  233. name = os.path.join(TMP, 'grail_scene33')
  234. ret = self.run_state(
  235. 'file.managed', name=name, mode='0770', source='salt://grail/scene33'
  236. )
  237. if IS_WINDOWS:
  238. expected = 'The \'mode\' option is not supported on Windows'
  239. self.assertEqual(ret[list(ret)[0]]['comment'], expected)
  240. self.assertSaltFalseReturn(ret)
  241. return
  242. resulting_mode = stat.S_IMODE(
  243. os.stat(name).st_mode
  244. )
  245. self.assertEqual(oct(desired_mode), oct(resulting_mode))
  246. self.assertSaltTrueReturn(ret)
  247. def test_managed_file_mode_keep(self):
  248. '''
  249. Test using "mode: keep" in a file.managed state
  250. '''
  251. _test_managed_file_mode_keep_helper(self, local=False)
  252. def test_managed_file_mode_keep_local_source(self):
  253. '''
  254. Test using "mode: keep" in a file.managed state, with a local file path
  255. as the source.
  256. '''
  257. _test_managed_file_mode_keep_helper(self, local=True)
  258. def test_managed_file_mode_file_exists_replace(self):
  259. '''
  260. file.managed, existing file with replace=True, change permissions
  261. '''
  262. initial_mode = 504 # 0770 octal
  263. desired_mode = 384 # 0600 octal
  264. name = os.path.join(TMP, 'grail_scene33')
  265. ret = self.run_state(
  266. 'file.managed', name=name, mode=oct(initial_mode), source='salt://grail/scene33'
  267. )
  268. if IS_WINDOWS:
  269. expected = 'The \'mode\' option is not supported on Windows'
  270. self.assertEqual(ret[list(ret)[0]]['comment'], expected)
  271. self.assertSaltFalseReturn(ret)
  272. return
  273. resulting_mode = stat.S_IMODE(
  274. os.stat(name).st_mode
  275. )
  276. self.assertEqual(oct(initial_mode), oct(resulting_mode))
  277. name = os.path.join(TMP, 'grail_scene33')
  278. ret = self.run_state(
  279. 'file.managed', name=name, replace=True, mode=oct(desired_mode), source='salt://grail/scene33'
  280. )
  281. resulting_mode = stat.S_IMODE(
  282. os.stat(name).st_mode
  283. )
  284. self.assertEqual(oct(desired_mode), oct(resulting_mode))
  285. self.assertSaltTrueReturn(ret)
  286. def test_managed_file_mode_file_exists_noreplace(self):
  287. '''
  288. file.managed, existing file with replace=False, change permissions
  289. '''
  290. initial_mode = 504 # 0770 octal
  291. desired_mode = 384 # 0600 octal
  292. name = os.path.join(TMP, 'grail_scene33')
  293. ret = self.run_state(
  294. 'file.managed', name=name, replace=True, mode=oct(initial_mode), source='salt://grail/scene33'
  295. )
  296. if IS_WINDOWS:
  297. expected = 'The \'mode\' option is not supported on Windows'
  298. self.assertEqual(ret[list(ret)[0]]['comment'], expected)
  299. self.assertSaltFalseReturn(ret)
  300. return
  301. ret = self.run_state(
  302. 'file.managed', name=name, replace=False, mode=oct(desired_mode), source='salt://grail/scene33'
  303. )
  304. resulting_mode = stat.S_IMODE(
  305. os.stat(name).st_mode
  306. )
  307. self.assertEqual(oct(desired_mode), oct(resulting_mode))
  308. self.assertSaltTrueReturn(ret)
  309. def test_managed_file_with_grains_data(self):
  310. '''
  311. Test to ensure we can render grains data into a managed
  312. file.
  313. '''
  314. grain_path = os.path.join(TMP, 'file-grain-test')
  315. state_file = 'file-grainget'
  316. self.run_function('state.sls', [state_file], pillar={'grain_path': grain_path})
  317. self.assertTrue(os.path.exists(grain_path))
  318. with salt.utils.files.fopen(grain_path, 'r') as fp_:
  319. file_contents = fp_.readlines()
  320. if IS_WINDOWS:
  321. match = '^minion\r\n'
  322. else:
  323. match = '^minion\n'
  324. self.assertTrue(re.match(match, file_contents[0]))
  325. def test_managed_file_with_pillar_sls(self):
  326. '''
  327. Test to ensure pillar data in sls file
  328. is rendered properly and file is created.
  329. '''
  330. state_name = 'file-pillarget'
  331. ret = self.run_function('state.sls', [state_name])
  332. self.assertSaltTrueReturn(ret)
  333. # Check to make sure the file was created
  334. check_file = self.run_function('file.file_exists', [FILEPILLAR])
  335. self.assertTrue(check_file)
  336. def test_managed_file_with_pillardefault_sls(self):
  337. '''
  338. Test to ensure when pillar data is not available
  339. in sls file with pillar.get it uses the default
  340. value.
  341. '''
  342. state_name = 'file-pillardefaultget'
  343. ret = self.run_function('state.sls', [state_name])
  344. self.assertSaltTrueReturn(ret)
  345. # Check to make sure the file was created
  346. check_file = self.run_function('file.file_exists', [FILEPILLARDEF])
  347. self.assertTrue(check_file)
  348. @skip_if_not_root
  349. def test_managed_dir_mode(self):
  350. '''
  351. Tests to ensure that file.managed creates directories with the
  352. permissions requested with the dir_mode argument
  353. '''
  354. desired_mode = 511 # 0777 in octal
  355. name = os.path.join(TMP, 'a', 'managed_dir_mode_test_file')
  356. desired_owner = 'nobody'
  357. ret = self.run_state(
  358. 'file.managed',
  359. name=name,
  360. source='salt://grail/scene33',
  361. mode=600,
  362. makedirs=True,
  363. user=desired_owner,
  364. dir_mode=oct(desired_mode) # 0777
  365. )
  366. if IS_WINDOWS:
  367. expected = 'The \'mode\' option is not supported on Windows'
  368. self.assertEqual(ret[list(ret)[0]]['comment'], expected)
  369. self.assertSaltFalseReturn(ret)
  370. return
  371. resulting_mode = stat.S_IMODE(
  372. os.stat(os.path.join(TMP, 'a')).st_mode
  373. )
  374. resulting_owner = pwd.getpwuid(os.stat(os.path.join(TMP, 'a')).st_uid).pw_name
  375. self.assertEqual(oct(desired_mode), oct(resulting_mode))
  376. self.assertSaltTrueReturn(ret)
  377. self.assertEqual(desired_owner, resulting_owner)
  378. def test_test_managed(self):
  379. '''
  380. file.managed test interface
  381. '''
  382. name = os.path.join(TMP, 'grail_not_not_scene33')
  383. ret = self.run_state(
  384. 'file.managed', test=True, name=name, source='salt://grail/scene33'
  385. )
  386. self.assertSaltNoneReturn(ret)
  387. self.assertFalse(os.path.isfile(name))
  388. def test_managed_show_changes_false(self):
  389. '''
  390. file.managed test interface
  391. '''
  392. name = os.path.join(TMP, 'grail_not_scene33')
  393. with salt.utils.files.fopen(name, 'wb') as fp_:
  394. fp_.write(b'test_managed_show_changes_false\n')
  395. ret = self.run_state(
  396. 'file.managed', name=name, source='salt://grail/scene33',
  397. show_changes=False
  398. )
  399. changes = next(six.itervalues(ret))['changes']
  400. self.assertEqual('<show_changes=False>', changes['diff'])
  401. def test_managed_show_changes_true(self):
  402. '''
  403. file.managed test interface
  404. '''
  405. name = os.path.join(TMP, 'grail_not_scene33')
  406. with salt.utils.files.fopen(name, 'wb') as fp_:
  407. fp_.write(b'test_managed_show_changes_false\n')
  408. ret = self.run_state(
  409. 'file.managed', name=name, source='salt://grail/scene33',
  410. )
  411. changes = next(six.itervalues(ret))['changes']
  412. self.assertIn('diff', changes)
  413. @skipIf(IS_WINDOWS, 'Don\'t know how to fix for Windows')
  414. def test_managed_escaped_file_path(self):
  415. '''
  416. file.managed test that 'salt://|' protects unusual characters in file path
  417. '''
  418. funny_file = salt.utils.files.mkstemp(prefix='?f!le? n@=3&', suffix='.file type')
  419. funny_file_name = os.path.split(funny_file)[1]
  420. funny_url = 'salt://|' + funny_file_name
  421. funny_url_path = os.path.join(BASE_FILES, funny_file_name)
  422. state_name = 'funny_file'
  423. state_file_name = state_name + '.sls'
  424. state_file = os.path.join(BASE_FILES, state_file_name)
  425. state_key = 'file_|-{0}_|-{0}_|-managed'.format(funny_file)
  426. self.addCleanup(os.remove, state_file)
  427. self.addCleanup(os.remove, funny_file)
  428. self.addCleanup(os.remove, funny_url_path)
  429. with salt.utils.files.fopen(funny_url_path, 'w'):
  430. pass
  431. with salt.utils.files.fopen(state_file, 'w') as fp_:
  432. fp_.write(textwrap.dedent('''\
  433. {0}:
  434. file.managed:
  435. - source: {1}
  436. - makedirs: True
  437. '''.format(funny_file, funny_url)))
  438. ret = self.run_function('state.sls', [state_name])
  439. self.assertTrue(ret[state_key]['result'])
  440. def test_managed_contents(self):
  441. '''
  442. test file.managed with contents that is a boolean, string, integer,
  443. float, list, and dictionary
  444. '''
  445. state_name = 'file-FileTest-test_managed_contents'
  446. state_filename = state_name + '.sls'
  447. state_file = os.path.join(BASE_FILES, state_filename)
  448. managed_files = {}
  449. state_keys = {}
  450. for typ in ('bool', 'str', 'int', 'float', 'list', 'dict'):
  451. managed_files[typ] = salt.utils.files.mkstemp()
  452. state_keys[typ] = 'file_|-{0} file_|-{1}_|-managed'.format(typ, managed_files[typ])
  453. try:
  454. with salt.utils.files.fopen(state_file, 'w') as fd_:
  455. fd_.write(textwrap.dedent('''\
  456. bool file:
  457. file.managed:
  458. - name: {bool}
  459. - contents: True
  460. str file:
  461. file.managed:
  462. - name: {str}
  463. - contents: Salt was here.
  464. int file:
  465. file.managed:
  466. - name: {int}
  467. - contents: 340282366920938463463374607431768211456
  468. float file:
  469. file.managed:
  470. - name: {float}
  471. - contents: 1.7518e-45 # gravitational coupling constant
  472. list file:
  473. file.managed:
  474. - name: {list}
  475. - contents: [1, 1, 2, 3, 5, 8, 13]
  476. dict file:
  477. file.managed:
  478. - name: {dict}
  479. - contents:
  480. C: charge
  481. P: parity
  482. T: time
  483. '''.format(**managed_files)))
  484. ret = self.run_function('state.sls', [state_name])
  485. self.assertSaltTrueReturn(ret)
  486. for typ in state_keys:
  487. self.assertTrue(ret[state_keys[typ]]['result'])
  488. self.assertIn('diff', ret[state_keys[typ]]['changes'])
  489. finally:
  490. if os.path.exists(state_file):
  491. os.remove(state_file)
  492. for typ in managed_files:
  493. if os.path.exists(managed_files[typ]):
  494. os.remove(managed_files[typ])
  495. @skip_if_not_root
  496. @skipIf(IS_WINDOWS, 'Windows does not support "mode" kwarg. Skipping.')
  497. @skipIf(not salt.utils.path.which('visudo'), 'sudo is missing')
  498. def test_managed_check_cmd(self):
  499. '''
  500. Test file.managed passing a basic check_cmd kwarg. See Issue #38111.
  501. '''
  502. r_group = 'root'
  503. if salt.utils.platform.is_darwin():
  504. r_group = 'wheel'
  505. try:
  506. ret = self.run_state(
  507. 'file.managed',
  508. name='/tmp/sudoers',
  509. user='root',
  510. group=r_group,
  511. mode=440,
  512. check_cmd='visudo -c -s -f'
  513. )
  514. self.assertSaltTrueReturn(ret)
  515. self.assertInSaltComment('Empty file', ret)
  516. self.assertEqual(ret['file_|-/tmp/sudoers_|-/tmp/sudoers_|-managed']['changes'],
  517. {'new': 'file /tmp/sudoers created', 'mode': '0440'})
  518. finally:
  519. # Clean Up File
  520. if os.path.exists('/tmp/sudoers'):
  521. os.remove('/tmp/sudoers')
  522. def test_managed_local_source_with_source_hash(self):
  523. '''
  524. Make sure that we enforce the source_hash even with local files
  525. '''
  526. name = os.path.join(TMP, 'local_source_with_source_hash')
  527. local_path = os.path.join(BASE_FILES, 'grail', 'scene33')
  528. actual_hash = '567fd840bf1548edc35c48eb66cdd78bfdfcccff'
  529. if IS_WINDOWS:
  530. # CRLF vs LF causes a different hash on windows
  531. actual_hash = 'f658a0ec121d9c17088795afcc6ff3c43cb9842a'
  532. # Reverse the actual hash
  533. bad_hash = actual_hash[::-1]
  534. def remove_file():
  535. try:
  536. os.remove(name)
  537. except OSError as exc:
  538. if exc.errno != errno.ENOENT:
  539. raise
  540. def do_test(clean=False):
  541. for proto in ('file://', ''):
  542. source = proto + local_path
  543. log.debug('Trying source %s', source)
  544. try:
  545. ret = self.run_state(
  546. 'file.managed',
  547. name=name,
  548. source=source,
  549. source_hash='sha1={0}'.format(bad_hash))
  550. self.assertSaltFalseReturn(ret)
  551. ret = ret[next(iter(ret))]
  552. # Shouldn't be any changes
  553. self.assertFalse(ret['changes'])
  554. # Check that we identified a hash mismatch
  555. self.assertIn(
  556. 'does not match actual checksum', ret['comment'])
  557. ret = self.run_state(
  558. 'file.managed',
  559. name=name,
  560. source=source,
  561. source_hash='sha1={0}'.format(actual_hash))
  562. self.assertSaltTrueReturn(ret)
  563. finally:
  564. if clean:
  565. remove_file()
  566. remove_file()
  567. log.debug('Trying with nonexistant destination file')
  568. do_test()
  569. log.debug('Trying with destination file already present')
  570. with salt.utils.files.fopen(name, 'w'):
  571. pass
  572. try:
  573. do_test(clean=False)
  574. finally:
  575. remove_file()
  576. def test_managed_local_source_does_not_exist(self):
  577. '''
  578. Make sure that we exit gracefully when a local source doesn't exist
  579. '''
  580. name = os.path.join(TMP, 'local_source_does_not_exist')
  581. local_path = os.path.join(BASE_FILES, 'grail', 'scene99')
  582. for proto in ('file://', ''):
  583. source = proto + local_path
  584. log.debug('Trying source %s', source)
  585. ret = self.run_state(
  586. 'file.managed',
  587. name=name,
  588. source=source)
  589. self.assertSaltFalseReturn(ret)
  590. ret = ret[next(iter(ret))]
  591. # Shouldn't be any changes
  592. self.assertFalse(ret['changes'])
  593. # Check that we identified a hash mismatch
  594. self.assertIn(
  595. 'does not exist', ret['comment'])
  596. def test_managed_unicode_jinja_with_tojson_filter(self):
  597. '''
  598. Using {{ varname }} with a list or dictionary which contains unicode
  599. types on Python 2 will result in Jinja rendering the "u" prefix on each
  600. string. This tests that using the "tojson" jinja filter will dump them
  601. to a format which can be successfully loaded by our YAML loader.
  602. The two lines that should end up being rendered are meant to test two
  603. issues that would trip up PyYAML if the "tojson" filter were not used:
  604. 1. A unicode string type would be loaded as a unicode literal with the
  605. leading "u" as well as the quotes, rather than simply being loaded
  606. as the proper unicode type which matches the content of the string
  607. literal. In other words, u'foo' would be loaded literally as
  608. u"u'foo'". This test includes actual non-ascii unicode in one of the
  609. strings to confirm that this also handles these international
  610. characters properly.
  611. 2. Any unicode string type (such as a URL) which contains a colon would
  612. cause a ScannerError in PyYAML, as it would be assumed to delimit a
  613. mapping node.
  614. Dumping the data structure to JSON using the "tojson" jinja filter
  615. should produce an inline data structure which is valid YAML and will be
  616. loaded properly by our YAML loader.
  617. '''
  618. test_file = os.path.join(TMP, 'test-tojson.txt')
  619. ret = self.run_function(
  620. 'state.apply',
  621. mods='tojson',
  622. pillar={'tojson-file': test_file})
  623. ret = ret[next(iter(ret))]
  624. assert ret['result'], ret
  625. with salt.utils.files.fopen(test_file, mode='rb') as fp_:
  626. managed = salt.utils.stringutils.to_unicode(fp_.read())
  627. expected = dedent('''\
  628. Die Webseite ist https://saltstack.com.
  629. Der Zucker ist süß.
  630. ''')
  631. assert managed == expected, '{0!r} != {1!r}'.format(managed, expected) # pylint: disable=repr-flag-used-in-string
  632. def test_managed_source_hash_indifferent_case(self):
  633. '''
  634. Test passing a source_hash as an uppercase hash.
  635. This is a regression test for Issue #38914 and Issue #48230 (test=true use).
  636. '''
  637. name = os.path.join(TMP, 'source_hash_indifferent_case')
  638. state_name = 'file_|-{0}_|' \
  639. '-{0}_|-managed'.format(name)
  640. local_path = os.path.join(BASE_FILES, 'hello_world.txt')
  641. actual_hash = 'c98c24b677eff44860afea6f493bbaec5bb1c4cbb209c6fc2bbb47f66ff2ad31'
  642. if IS_WINDOWS:
  643. # CRLF vs LF causes a differnt hash on windows
  644. actual_hash = '92b772380a3f8e27a93e57e6deeca6c01da07f5aadce78bb2fbb20de10a66925'
  645. uppercase_hash = actual_hash.upper()
  646. try:
  647. # Lay down tmp file to test against
  648. self.run_state(
  649. 'file.managed',
  650. name=name,
  651. source=local_path,
  652. source_hash=actual_hash
  653. )
  654. # Test uppercase source_hash: should return True with no changes
  655. ret = self.run_state(
  656. 'file.managed',
  657. name=name,
  658. source=local_path,
  659. source_hash=uppercase_hash
  660. )
  661. assert ret[state_name]['result'] is True
  662. assert ret[state_name]['changes'] == {}
  663. # Test uppercase source_hash using test=true
  664. # Should return True with no changes
  665. ret = self.run_state(
  666. 'file.managed',
  667. name=name,
  668. source=local_path,
  669. source_hash=uppercase_hash,
  670. test=True
  671. )
  672. assert ret[state_name]['result'] is True
  673. assert ret[state_name]['changes'] == {}
  674. finally:
  675. # Clean Up File
  676. if os.path.exists(name):
  677. os.remove(name)
  678. @with_tempfile(create=False)
  679. def test_managed_latin1_diff(self, name):
  680. '''
  681. Tests that latin-1 file contents are represented properly in the diff
  682. '''
  683. # Lay down the initial file
  684. ret = self.run_state(
  685. 'file.managed',
  686. name=name,
  687. source='salt://issue-48777/old.html')
  688. ret = ret[next(iter(ret))]
  689. assert ret['result'] is True, ret
  690. # Replace it with the new file and check the diff
  691. ret = self.run_state(
  692. 'file.managed',
  693. name=name,
  694. source='salt://issue-48777/new.html')
  695. ret = ret[next(iter(ret))]
  696. assert ret['result'] is True, ret
  697. diff_lines = ret['changes']['diff'].split(os.linesep)
  698. assert '+räksmörgås' in diff_lines, diff_lines
  699. @with_tempfile()
  700. def test_managed_keep_source_false_salt(self, name):
  701. '''
  702. This test ensures that we properly clean the cached file if keep_source
  703. is set to False, for source files using a salt:// URL
  704. '''
  705. source = 'salt://grail/scene33'
  706. saltenv = 'base'
  707. # Run the state
  708. ret = self.run_state(
  709. 'file.managed',
  710. name=name,
  711. source=source,
  712. saltenv=saltenv,
  713. keep_source=False)
  714. ret = ret[next(iter(ret))]
  715. assert ret['result'] is True
  716. # Now make sure that the file is not cached
  717. result = self.run_function('cp.is_cached', [source, saltenv])
  718. assert result == '', 'File is still cached at {0}'.format(result)
  719. @with_tempfile(create=False)
  720. @with_tempfile(create=False)
  721. def test_file_managed_onchanges(self, file1, file2):
  722. '''
  723. Test file.managed state with onchanges
  724. '''
  725. pillar = {'file1': file1,
  726. 'file2': file2,
  727. 'source': 'salt://testfile',
  728. 'req': 'onchanges'}
  729. # Lay down the file used in the below SLS to ensure that when it is
  730. # run, there are no changes.
  731. self.run_state(
  732. 'file.managed',
  733. name=pillar['file2'],
  734. source=pillar['source'])
  735. ret = self.repack_state_returns(
  736. self.run_function(
  737. 'state.apply',
  738. mods='onchanges_prereq',
  739. pillar=pillar,
  740. test=True,
  741. )
  742. )
  743. # The file states should both exit with None
  744. assert ret['one']['result'] is None, ret['one']['result']
  745. assert ret['three']['result'] is True, ret['three']['result']
  746. # The first file state should have changes, since a new file was
  747. # created. The other one should not, since we already created that file
  748. # before applying the SLS file.
  749. assert ret['one']['changes']
  750. assert not ret['three']['changes'], ret['three']['changes']
  751. # The state watching 'one' should have been run due to changes
  752. assert ret['two']['comment'] == 'Success!', ret['two']['comment']
  753. # The state watching 'three' should not have been run
  754. assert ret['four']['comment'] == \
  755. 'State was not run because none of the onchanges reqs changed', \
  756. ret['four']['comment']
  757. @with_tempfile(create=False)
  758. @with_tempfile(create=False)
  759. def test_file_managed_prereq(self, file1, file2):
  760. '''
  761. Test file.managed state with prereq
  762. '''
  763. pillar = {'file1': file1,
  764. 'file2': file2,
  765. 'source': 'salt://testfile',
  766. 'req': 'prereq'}
  767. # Lay down the file used in the below SLS to ensure that when it is
  768. # run, there are no changes.
  769. self.run_state(
  770. 'file.managed',
  771. name=pillar['file2'],
  772. source=pillar['source'])
  773. ret = self.repack_state_returns(
  774. self.run_function(
  775. 'state.apply',
  776. mods='onchanges_prereq',
  777. pillar=pillar,
  778. test=True,
  779. )
  780. )
  781. # The file states should both exit with None
  782. assert ret['one']['result'] is None, ret['one']['result']
  783. assert ret['three']['result'] is True, ret['three']['result']
  784. # The first file state should have changes, since a new file was
  785. # created. The other one should not, since we already created that file
  786. # before applying the SLS file.
  787. assert ret['one']['changes']
  788. assert not ret['three']['changes'], ret['three']['changes']
  789. # The state watching 'one' should have been run due to changes
  790. assert ret['two']['comment'] == 'Success!', ret['two']['comment']
  791. # The state watching 'three' should not have been run
  792. assert ret['four']['comment'] == 'No changes detected', \
  793. ret['four']['comment']
  794. def test_directory(self):
  795. '''
  796. file.directory
  797. '''
  798. name = os.path.join(TMP, 'a_new_dir')
  799. ret = self.run_state('file.directory', name=name)
  800. self.assertSaltTrueReturn(ret)
  801. self.assertTrue(os.path.isdir(name))
  802. def test_directory_symlink_dry_run(self):
  803. '''
  804. Ensure that symlinks are followed when file.directory is run with
  805. test=True
  806. '''
  807. try:
  808. tmp_dir = os.path.join(TMP, 'pgdata')
  809. sym_dir = os.path.join(TMP, 'pg_data')
  810. os.mkdir(tmp_dir, 0o700)
  811. self.run_function('file.symlink', [tmp_dir, sym_dir])
  812. if IS_WINDOWS:
  813. ret = self.run_state(
  814. 'file.directory', test=True, name=sym_dir,
  815. follow_symlinks=True, win_owner='Administrators')
  816. else:
  817. ret = self.run_state(
  818. 'file.directory', test=True, name=sym_dir,
  819. follow_symlinks=True, mode=700)
  820. self.assertSaltTrueReturn(ret)
  821. finally:
  822. if os.path.isdir(tmp_dir):
  823. self.run_function('file.remove', [tmp_dir])
  824. if os.path.islink(sym_dir):
  825. self.run_function('file.remove', [sym_dir])
  826. @skip_if_not_root
  827. @skipIf(IS_WINDOWS, 'Mode not available in Windows')
  828. def test_directory_max_depth(self):
  829. '''
  830. file.directory
  831. Test the max_depth option by iteratively increasing the depth and
  832. checking that no changes deeper than max_depth have been attempted
  833. '''
  834. def _get_oct_mode(name):
  835. '''
  836. Return a string octal representation of the permissions for name
  837. '''
  838. return salt.utils.files.normalize_mode(oct(os.stat(name).st_mode & 0o777))
  839. top = os.path.join(TMP, 'top_dir')
  840. sub = os.path.join(top, 'sub_dir')
  841. subsub = os.path.join(sub, 'sub_sub_dir')
  842. dirs = [top, sub, subsub]
  843. initial_mode = '0111'
  844. changed_mode = '0555'
  845. initial_modes = {0: {sub: '0755',
  846. subsub: '0111'},
  847. 1: {sub: '0111',
  848. subsub: '0111'},
  849. 2: {sub: '0111',
  850. subsub: '0111'}}
  851. if not os.path.isdir(subsub):
  852. os.makedirs(subsub, int(initial_mode, 8))
  853. try:
  854. for depth in range(0, 3):
  855. ret = self.run_state('file.directory',
  856. name=top,
  857. max_depth=depth,
  858. dir_mode=changed_mode,
  859. recurse=['mode'])
  860. self.assertSaltTrueReturn(ret)
  861. for changed_dir in dirs[0:depth+1]:
  862. self.assertEqual(changed_mode,
  863. _get_oct_mode(changed_dir))
  864. for untouched_dir in dirs[depth+1:]:
  865. # Beginning in Python 3.7, os.makedirs no longer sets
  866. # the mode of intermediate directories to the mode that
  867. # is passed.
  868. if sys.version_info >= (3, 7):
  869. _mode = initial_modes[depth][untouched_dir]
  870. self.assertEqual(_mode,
  871. _get_oct_mode(untouched_dir))
  872. else:
  873. self.assertEqual(initial_mode,
  874. _get_oct_mode(untouched_dir))
  875. finally:
  876. shutil.rmtree(top)
  877. def test_test_directory(self):
  878. '''
  879. file.directory
  880. '''
  881. name = os.path.join(TMP, 'a_not_dir')
  882. ret = self.run_state('file.directory', test=True, name=name)
  883. self.assertSaltNoneReturn(ret)
  884. self.assertFalse(os.path.isdir(name))
  885. @with_tempdir()
  886. def test_directory_clean(self, base_dir):
  887. '''
  888. file.directory with clean=True
  889. '''
  890. name = os.path.join(base_dir, 'directory_clean_dir')
  891. os.mkdir(name)
  892. strayfile = os.path.join(name, 'strayfile')
  893. with salt.utils.files.fopen(strayfile, 'w'):
  894. pass
  895. straydir = os.path.join(name, 'straydir')
  896. if not os.path.isdir(straydir):
  897. os.makedirs(straydir)
  898. with salt.utils.files.fopen(os.path.join(straydir, 'strayfile2'), 'w'):
  899. pass
  900. ret = self.run_state('file.directory', name=name, clean=True)
  901. self.assertSaltTrueReturn(ret)
  902. self.assertFalse(os.path.exists(strayfile))
  903. self.assertFalse(os.path.exists(straydir))
  904. self.assertTrue(os.path.isdir(name))
  905. def test_directory_is_idempotent(self):
  906. '''
  907. Ensure the file.directory state produces no changes when rerun.
  908. '''
  909. name = os.path.join(TMP, 'a_dir_twice')
  910. if IS_WINDOWS:
  911. username = os.environ.get('USERNAME', 'Administrators')
  912. domain = os.environ.get('USERDOMAIN', '')
  913. fullname = '{0}\\{1}'.format(domain, username)
  914. ret = self.run_state('file.directory', name=name, win_owner=fullname)
  915. else:
  916. ret = self.run_state('file.directory', name=name)
  917. self.assertSaltTrueReturn(ret)
  918. if IS_WINDOWS:
  919. ret = self.run_state('file.directory', name=name, win_owner=username)
  920. else:
  921. ret = self.run_state('file.directory', name=name)
  922. self.assertSaltTrueReturn(ret)
  923. self.assertSaltStateChangesEqual(ret, {})
  924. @with_tempdir()
  925. def test_directory_clean_exclude(self, base_dir):
  926. '''
  927. file.directory with clean=True and exclude_pat set
  928. '''
  929. name = os.path.join(base_dir, 'directory_clean_dir')
  930. if not os.path.isdir(name):
  931. os.makedirs(name)
  932. strayfile = os.path.join(name, 'strayfile')
  933. with salt.utils.files.fopen(strayfile, 'w'):
  934. pass
  935. straydir = os.path.join(name, 'straydir')
  936. if not os.path.isdir(straydir):
  937. os.makedirs(straydir)
  938. strayfile2 = os.path.join(straydir, 'strayfile2')
  939. with salt.utils.files.fopen(strayfile2, 'w'):
  940. pass
  941. keepfile = os.path.join(straydir, 'keepfile')
  942. with salt.utils.files.fopen(keepfile, 'w'):
  943. pass
  944. exclude_pat = 'E@^straydir(|/keepfile)$'
  945. if IS_WINDOWS:
  946. exclude_pat = 'E@^straydir(|\\\\keepfile)$'
  947. ret = self.run_state('file.directory',
  948. name=name,
  949. clean=True,
  950. exclude_pat=exclude_pat)
  951. self.assertSaltTrueReturn(ret)
  952. self.assertFalse(os.path.exists(strayfile))
  953. self.assertFalse(os.path.exists(strayfile2))
  954. self.assertTrue(os.path.exists(keepfile))
  955. @skipIf(IS_WINDOWS, 'Skip on windows')
  956. @with_tempdir()
  957. def test_test_directory_clean_exclude(self, base_dir):
  958. '''
  959. file.directory with test=True, clean=True and exclude_pat set
  960. Skipped on windows because clean and exclude_pat not supported by
  961. salt.sates.file._check_directory_win
  962. '''
  963. name = os.path.join(base_dir, 'directory_clean_dir')
  964. os.mkdir(name)
  965. strayfile = os.path.join(name, 'strayfile')
  966. with salt.utils.files.fopen(strayfile, 'w'):
  967. pass
  968. straydir = os.path.join(name, 'straydir')
  969. if not os.path.isdir(straydir):
  970. os.makedirs(straydir)
  971. strayfile2 = os.path.join(straydir, 'strayfile2')
  972. with salt.utils.files.fopen(strayfile2, 'w'):
  973. pass
  974. keepfile = os.path.join(straydir, 'keepfile')
  975. with salt.utils.files.fopen(keepfile, 'w'):
  976. pass
  977. exclude_pat = 'E@^straydir(|/keepfile)$'
  978. if IS_WINDOWS:
  979. exclude_pat = 'E@^straydir(|\\\\keepfile)$'
  980. ret = self.run_state('file.directory',
  981. test=True,
  982. name=name,
  983. clean=True,
  984. exclude_pat=exclude_pat)
  985. comment = next(six.itervalues(ret))['comment']
  986. self.assertSaltNoneReturn(ret)
  987. self.assertTrue(os.path.exists(strayfile))
  988. self.assertTrue(os.path.exists(strayfile2))
  989. self.assertTrue(os.path.exists(keepfile))
  990. self.assertIn(strayfile, comment)
  991. self.assertIn(strayfile2, comment)
  992. self.assertNotIn(keepfile, comment)
  993. @with_tempdir()
  994. def test_directory_clean_require_in(self, name):
  995. '''
  996. file.directory test with clean=True and require_in file
  997. '''
  998. state_name = 'file-FileTest-test_directory_clean_require_in'
  999. state_filename = state_name + '.sls'
  1000. state_file = os.path.join(BASE_FILES, state_filename)
  1001. wrong_file = os.path.join(name, "wrong")
  1002. with salt.utils.files.fopen(wrong_file, "w") as fp:
  1003. fp.write("foo")
  1004. good_file = os.path.join(name, "bar")
  1005. with salt.utils.files.fopen(state_file, 'w') as fp:
  1006. self.addCleanup(lambda: os.remove(state_file))
  1007. fp.write(textwrap.dedent('''\
  1008. some_dir:
  1009. file.directory:
  1010. - name: {name}
  1011. - clean: true
  1012. {good_file}:
  1013. file.managed:
  1014. - require_in:
  1015. - file: some_dir
  1016. '''.format(name=name, good_file=good_file)))
  1017. ret = self.run_function('state.sls', [state_name])
  1018. self.assertTrue(os.path.exists(good_file))
  1019. self.assertFalse(os.path.exists(wrong_file))
  1020. @with_tempdir()
  1021. def test_directory_clean_require_in_with_id(self, name):
  1022. '''
  1023. file.directory test with clean=True and require_in file with an ID
  1024. different from the file name
  1025. '''
  1026. state_name = 'file-FileTest-test_directory_clean_require_in_with_id'
  1027. state_filename = state_name + '.sls'
  1028. state_file = os.path.join(BASE_FILES, state_filename)
  1029. wrong_file = os.path.join(name, "wrong")
  1030. with salt.utils.files.fopen(wrong_file, "w") as fp:
  1031. fp.write("foo")
  1032. good_file = os.path.join(name, "bar")
  1033. with salt.utils.files.fopen(state_file, 'w') as fp:
  1034. self.addCleanup(lambda: os.remove(state_file))
  1035. fp.write(textwrap.dedent('''\
  1036. some_dir:
  1037. file.directory:
  1038. - name: {name}
  1039. - clean: true
  1040. some_file:
  1041. file.managed:
  1042. - name: {good_file}
  1043. - require_in:
  1044. - file: some_dir
  1045. '''.format(name=name, good_file=good_file)))
  1046. ret = self.run_function('state.sls', [state_name])
  1047. self.assertTrue(os.path.exists(good_file))
  1048. self.assertFalse(os.path.exists(wrong_file))
  1049. @skipIf(salt.utils.platform.is_darwin(), 'WAR ROOM TEMPORARY SKIP, Test is flaky on macosx')
  1050. @with_tempdir()
  1051. def test_directory_clean_require_with_name(self, name):
  1052. '''
  1053. file.directory test with clean=True and require with a file state
  1054. relatively to the state's name, not its ID.
  1055. '''
  1056. state_name = 'file-FileTest-test_directory_clean_require_in_with_id'
  1057. state_filename = state_name + '.sls'
  1058. state_file = os.path.join(BASE_FILES, state_filename)
  1059. wrong_file = os.path.join(name, "wrong")
  1060. with salt.utils.files.fopen(wrong_file, "w") as fp:
  1061. fp.write("foo")
  1062. good_file = os.path.join(name, "bar")
  1063. with salt.utils.files.fopen(state_file, 'w') as fp:
  1064. self.addCleanup(lambda: os.remove(state_file))
  1065. fp.write(textwrap.dedent('''\
  1066. some_dir:
  1067. file.directory:
  1068. - name: {name}
  1069. - clean: true
  1070. - require:
  1071. # This requirement refers to the name of the following
  1072. # state, not its ID.
  1073. - file: {good_file}
  1074. some_file:
  1075. file.managed:
  1076. - name: {good_file}
  1077. '''.format(name=name, good_file=good_file)))
  1078. ret = self.run_function('state.sls', [state_name])
  1079. self.assertTrue(os.path.exists(good_file))
  1080. self.assertFalse(os.path.exists(wrong_file))
  1081. def test_directory_broken_symlink(self):
  1082. '''
  1083. Ensure that file.directory works even if a directory
  1084. contains broken symbolic link
  1085. '''
  1086. try:
  1087. tmp_dir = os.path.join(TMP, 'foo')
  1088. null_file = '{0}/null'.format(tmp_dir)
  1089. broken_link = '{0}/broken'.format(tmp_dir)
  1090. os.mkdir(tmp_dir, 0o700)
  1091. self.run_function('file.symlink', [null_file, broken_link])
  1092. if IS_WINDOWS:
  1093. ret = self.run_state(
  1094. 'file.directory', name=tmp_dir, recurse=['mode'],
  1095. follow_symlinks=True, win_owner='Administrators')
  1096. else:
  1097. ret = self.run_state(
  1098. 'file.directory', name=tmp_dir, recurse=['mode'],
  1099. file_mode=644, dir_mode=755)
  1100. self.assertSaltTrueReturn(ret)
  1101. finally:
  1102. if os.path.isdir(tmp_dir):
  1103. self.run_function('file.remove', [tmp_dir])
  1104. @with_tempdir(create=False)
  1105. def test_recurse(self, name):
  1106. '''
  1107. file.recurse
  1108. '''
  1109. ret = self.run_state('file.recurse', name=name, source='salt://grail')
  1110. self.assertSaltTrueReturn(ret)
  1111. self.assertTrue(os.path.isfile(os.path.join(name, '36', 'scene')))
  1112. @with_tempdir(create=False)
  1113. @with_tempdir(create=False)
  1114. def test_recurse_specific_env(self, dir1, dir2):
  1115. '''
  1116. file.recurse passing __env__
  1117. '''
  1118. ret = self.run_state('file.recurse',
  1119. name=dir1,
  1120. source='salt://holy',
  1121. __env__='prod')
  1122. self.assertSaltTrueReturn(ret)
  1123. self.assertTrue(os.path.isfile(os.path.join(dir1, '32', 'scene')))
  1124. ret = self.run_state('file.recurse',
  1125. name=dir2,
  1126. source='salt://holy',
  1127. saltenv='prod')
  1128. self.assertSaltTrueReturn(ret)
  1129. self.assertTrue(os.path.isfile(os.path.join(dir2, '32', 'scene')))
  1130. @with_tempdir(create=False)
  1131. @with_tempdir(create=False)
  1132. def test_recurse_specific_env_in_url(self, dir1, dir2):
  1133. '''
  1134. file.recurse passing __env__
  1135. '''
  1136. ret = self.run_state('file.recurse',
  1137. name=dir1,
  1138. source='salt://holy?saltenv=prod')
  1139. self.assertSaltTrueReturn(ret)
  1140. self.assertTrue(os.path.isfile(os.path.join(dir1, '32', 'scene')))
  1141. ret = self.run_state('file.recurse',
  1142. name=dir2,
  1143. source='salt://holy?saltenv=prod')
  1144. self.assertSaltTrueReturn(ret)
  1145. self.assertTrue(os.path.isfile(os.path.join(dir2, '32', 'scene')))
  1146. @with_tempdir(create=False)
  1147. def test_test_recurse(self, name):
  1148. '''
  1149. file.recurse test interface
  1150. '''
  1151. ret = self.run_state(
  1152. 'file.recurse', test=True, name=name, source='salt://grail',
  1153. )
  1154. self.assertSaltNoneReturn(ret)
  1155. self.assertFalse(os.path.isfile(os.path.join(name, '36', 'scene')))
  1156. self.assertFalse(os.path.exists(name))
  1157. @with_tempdir(create=False)
  1158. @with_tempdir(create=False)
  1159. def test_test_recurse_specific_env(self, dir1, dir2):
  1160. '''
  1161. file.recurse test interface
  1162. '''
  1163. ret = self.run_state('file.recurse',
  1164. test=True,
  1165. name=dir1,
  1166. source='salt://holy',
  1167. __env__='prod'
  1168. )
  1169. self.assertSaltNoneReturn(ret)
  1170. self.assertFalse(os.path.isfile(os.path.join(dir1, '32', 'scene')))
  1171. self.assertFalse(os.path.exists(dir1))
  1172. ret = self.run_state('file.recurse',
  1173. test=True,
  1174. name=dir2,
  1175. source='salt://holy',
  1176. saltenv='prod'
  1177. )
  1178. self.assertSaltNoneReturn(ret)
  1179. self.assertFalse(os.path.isfile(os.path.join(dir2, '32', 'scene')))
  1180. self.assertFalse(os.path.exists(dir2))
  1181. @with_tempdir(create=False)
  1182. def test_recurse_template(self, name):
  1183. '''
  1184. file.recurse with jinja template enabled
  1185. '''
  1186. _ts = 'TEMPLATE TEST STRING'
  1187. ret = self.run_state(
  1188. 'file.recurse', name=name, source='salt://grail',
  1189. template='jinja', defaults={'spam': _ts})
  1190. self.assertSaltTrueReturn(ret)
  1191. with salt.utils.files.fopen(os.path.join(name, 'scene33'), 'r') as fp_:
  1192. contents = fp_.read()
  1193. self.assertIn(_ts, contents)
  1194. @with_tempdir()
  1195. def test_recurse_clean(self, name):
  1196. '''
  1197. file.recurse with clean=True
  1198. '''
  1199. strayfile = os.path.join(name, 'strayfile')
  1200. with salt.utils.files.fopen(strayfile, 'w'):
  1201. pass
  1202. # Corner cases: replacing file with a directory and vice versa
  1203. with salt.utils.files.fopen(os.path.join(name, '36'), 'w'):
  1204. pass
  1205. os.makedirs(os.path.join(name, 'scene33'))
  1206. ret = self.run_state(
  1207. 'file.recurse', name=name, source='salt://grail', clean=True)
  1208. self.assertSaltTrueReturn(ret)
  1209. self.assertFalse(os.path.exists(strayfile))
  1210. self.assertTrue(os.path.isfile(os.path.join(name, '36', 'scene')))
  1211. self.assertTrue(os.path.isfile(os.path.join(name, 'scene33')))
  1212. @with_tempdir()
  1213. def test_recurse_clean_specific_env(self, name):
  1214. '''
  1215. file.recurse with clean=True and __env__=prod
  1216. '''
  1217. strayfile = os.path.join(name, 'strayfile')
  1218. with salt.utils.files.fopen(strayfile, 'w'):
  1219. pass
  1220. # Corner cases: replacing file with a directory and vice versa
  1221. with salt.utils.files.fopen(os.path.join(name, '32'), 'w'):
  1222. pass
  1223. os.makedirs(os.path.join(name, 'scene34'))
  1224. ret = self.run_state('file.recurse',
  1225. name=name,
  1226. source='salt://holy',
  1227. clean=True,
  1228. __env__='prod')
  1229. self.assertSaltTrueReturn(ret)
  1230. self.assertFalse(os.path.exists(strayfile))
  1231. self.assertTrue(os.path.isfile(os.path.join(name, '32', 'scene')))
  1232. self.assertTrue(os.path.isfile(os.path.join(name, 'scene34')))
  1233. @skipIf(IS_WINDOWS, 'Skip on windows')
  1234. @with_tempdir()
  1235. def test_recurse_issue_34945(self, base_dir):
  1236. '''
  1237. This tests the case where the source dir for the file.recurse state
  1238. does not contain any files (only subdirectories), and the dir_mode is
  1239. being managed. For a long time, this corner case resulted in the top
  1240. level of the destination directory being created with the wrong initial
  1241. permissions, a problem that would be corrected later on in the
  1242. file.recurse state via running state.directory. However, the
  1243. file.directory state only gets called when there are files to be
  1244. managed in that directory, and when the source directory contains only
  1245. subdirectories, the incorrectly-set initial perms would not be
  1246. repaired.
  1247. This was fixed in https://github.com/saltstack/salt/pull/35309
  1248. Skipped on windows because dir_mode is not supported.
  1249. '''
  1250. dir_mode = '2775'
  1251. issue_dir = 'issue-34945'
  1252. name = os.path.join(base_dir, issue_dir)
  1253. ret = self.run_state('file.recurse',
  1254. name=name,
  1255. source='salt://' + issue_dir,
  1256. dir_mode=dir_mode)
  1257. self.assertSaltTrueReturn(ret)
  1258. actual_dir_mode = oct(stat.S_IMODE(os.stat(name).st_mode))[-4:]
  1259. self.assertEqual(dir_mode, actual_dir_mode)
  1260. @with_tempdir(create=False)
  1261. def test_recurse_issue_40578(self, name):
  1262. '''
  1263. This ensures that the state doesn't raise an exception when it
  1264. encounters a file with a unicode filename in the process of invoking
  1265. file.source_list.
  1266. '''
  1267. ret = self.run_state('file.recurse',
  1268. name=name,
  1269. source='salt://соль')
  1270. self.assertSaltTrueReturn(ret)
  1271. if six.PY2 and IS_WINDOWS:
  1272. # Providing unicode to os.listdir so that we avoid having listdir
  1273. # try to decode the filenames using the systemencoding on windows
  1274. # python 2.
  1275. files = os.listdir(name.decode('utf-8'))
  1276. else:
  1277. files = salt.utils.data.decode(os.listdir(name), normalize=True)
  1278. self.assertEqual(
  1279. sorted(files),
  1280. sorted(['foo.txt', 'спам.txt', 'яйца.txt']),
  1281. )
  1282. @with_tempfile()
  1283. def test_replace(self, name):
  1284. '''
  1285. file.replace
  1286. '''
  1287. with salt.utils.files.fopen(name, 'w+') as fp_:
  1288. fp_.write('change_me')
  1289. ret = self.run_state('file.replace',
  1290. name=name, pattern='change', repl='salt', backup=False)
  1291. with salt.utils.files.fopen(name, 'r') as fp_:
  1292. self.assertIn('salt', fp_.read())
  1293. self.assertSaltTrueReturn(ret)
  1294. @with_tempdir()
  1295. def test_replace_issue_18612(self, base_dir):
  1296. '''
  1297. Test the (mis-)behaviour of file.replace as described in #18612:
  1298. Using 'prepend_if_not_found' or 'append_if_not_found' resulted in
  1299. an infinitely growing file as 'file.replace' didn't check beforehand
  1300. whether the changes had already been done to the file
  1301. # Case description:
  1302. The tested file contains one commented line
  1303. The commented line should be uncommented in the end, nothing else should change
  1304. '''
  1305. test_name = 'test_replace_issue_18612'
  1306. path_test = os.path.join(base_dir, test_name)
  1307. with salt.utils.files.fopen(path_test, 'w+') as fp_test_:
  1308. fp_test_.write('# en_US.UTF-8')
  1309. ret = []
  1310. for x in range(0, 3):
  1311. ret.append(self.run_state('file.replace',
  1312. name=path_test, pattern='^# en_US.UTF-8$', repl='en_US.UTF-8', append_if_not_found=True))
  1313. # ensure, the number of lines didn't change, even after invoking 'file.replace' 3 times
  1314. with salt.utils.files.fopen(path_test, 'r') as fp_test_:
  1315. self.assertTrue((sum(1 for _ in fp_test_) == 1))
  1316. # ensure, the replacement succeeded
  1317. with salt.utils.files.fopen(path_test, 'r') as fp_test_:
  1318. self.assertTrue(fp_test_.read().startswith('en_US.UTF-8'))
  1319. # ensure, all runs of 'file.replace' reported success
  1320. for item in ret:
  1321. self.assertSaltTrueReturn(item)
  1322. @with_tempdir()
  1323. def test_replace_issue_18612_prepend(self, base_dir):
  1324. '''
  1325. Test the (mis-)behaviour of file.replace as described in #18612:
  1326. Using 'prepend_if_not_found' or 'append_if_not_found' resulted in
  1327. an infinitely growing file as 'file.replace' didn't check beforehand
  1328. whether the changes had already been done to the file
  1329. # Case description:
  1330. The tested multifile contains multiple lines not matching the pattern or replacement in any way
  1331. The replacement pattern should be prepended to the file
  1332. '''
  1333. test_name = 'test_replace_issue_18612_prepend'
  1334. path_in = os.path.join(
  1335. FILES, 'file.replace', '{0}.in'.format(test_name)
  1336. )
  1337. path_out = os.path.join(
  1338. FILES, 'file.replace', '{0}.out'.format(test_name)
  1339. )
  1340. path_test = os.path.join(base_dir, test_name)
  1341. # create test file based on initial template
  1342. shutil.copyfile(path_in, path_test)
  1343. ret = []
  1344. for x in range(0, 3):
  1345. ret.append(self.run_state('file.replace',
  1346. name=path_test, pattern='^# en_US.UTF-8$', repl='en_US.UTF-8', prepend_if_not_found=True))
  1347. # ensure, the resulting file contains the expected lines
  1348. self.assertTrue(filecmp.cmp(path_test, path_out))
  1349. # ensure the initial file was properly backed up
  1350. self.assertTrue(filecmp.cmp(path_test + '.bak', path_in))
  1351. # ensure, all runs of 'file.replace' reported success
  1352. for item in ret:
  1353. self.assertSaltTrueReturn(item)
  1354. @with_tempdir()
  1355. def test_replace_issue_18612_append(self, base_dir):
  1356. '''
  1357. Test the (mis-)behaviour of file.replace as described in #18612:
  1358. Using 'prepend_if_not_found' or 'append_if_not_found' resulted in
  1359. an infinitely growing file as 'file.replace' didn't check beforehand
  1360. whether the changes had already been done to the file
  1361. # Case description:
  1362. The tested multifile contains multiple lines not matching the pattern or replacement in any way
  1363. The replacement pattern should be appended to the file
  1364. '''
  1365. test_name = 'test_replace_issue_18612_append'
  1366. path_in = os.path.join(
  1367. FILES, 'file.replace', '{0}.in'.format(test_name)
  1368. )
  1369. path_out = os.path.join(
  1370. FILES, 'file.replace', '{0}.out'.format(test_name)
  1371. )
  1372. path_test = os.path.join(base_dir, test_name)
  1373. # create test file based on initial template
  1374. shutil.copyfile(path_in, path_test)
  1375. ret = []
  1376. for x in range(0, 3):
  1377. ret.append(self.run_state('file.replace',
  1378. name=path_test, pattern='^# en_US.UTF-8$', repl='en_US.UTF-8', append_if_not_found=True))
  1379. # ensure, the resulting file contains the expected lines
  1380. self.assertTrue(filecmp.cmp(path_test, path_out))
  1381. # ensure the initial file was properly backed up
  1382. self.assertTrue(filecmp.cmp(path_test + '.bak', path_in))
  1383. # ensure, all runs of 'file.replace' reported success
  1384. for item in ret:
  1385. self.assertSaltTrueReturn(item)
  1386. @with_tempdir()
  1387. def test_replace_issue_18612_append_not_found_content(self, base_dir):
  1388. '''
  1389. Test the (mis-)behaviour of file.replace as described in #18612:
  1390. Using 'prepend_if_not_found' or 'append_if_not_found' resulted in
  1391. an infinitely growing file as 'file.replace' didn't check beforehand
  1392. whether the changes had already been done to the file
  1393. # Case description:
  1394. The tested multifile contains multiple lines not matching the pattern or replacement in any way
  1395. The 'not_found_content' value should be appended to the file
  1396. '''
  1397. test_name = 'test_replace_issue_18612_append_not_found_content'
  1398. path_in = os.path.join(
  1399. FILES, 'file.replace', '{0}.in'.format(test_name)
  1400. )
  1401. path_out = os.path.join(
  1402. FILES, 'file.replace', '{0}.out'.format(test_name)
  1403. )
  1404. path_test = os.path.join(base_dir, test_name)
  1405. # create test file based on initial template
  1406. shutil.copyfile(path_in, path_test)
  1407. ret = []
  1408. for x in range(0, 3):
  1409. ret.append(
  1410. self.run_state('file.replace',
  1411. name=path_test,
  1412. pattern='^# en_US.UTF-8$',
  1413. repl='en_US.UTF-8',
  1414. append_if_not_found=True,
  1415. not_found_content='THIS LINE WASN\'T FOUND! SO WE\'RE APPENDING IT HERE!'
  1416. ))
  1417. # ensure, the resulting file contains the expected lines
  1418. self.assertTrue(filecmp.cmp(path_test, path_out))
  1419. # ensure the initial file was properly backed up
  1420. self.assertTrue(filecmp.cmp(path_test + '.bak', path_in))
  1421. # ensure, all runs of 'file.replace' reported success
  1422. for item in ret:
  1423. self.assertSaltTrueReturn(item)
  1424. @with_tempdir()
  1425. def test_replace_issue_18612_change_mid_line_with_comment(self, base_dir):
  1426. '''
  1427. Test the (mis-)behaviour of file.replace as described in #18612:
  1428. Using 'prepend_if_not_found' or 'append_if_not_found' resulted in
  1429. an infinitely growing file as 'file.replace' didn't check beforehand
  1430. whether the changes had already been done to the file
  1431. # Case description:
  1432. The tested file contains 5 key=value pairs
  1433. The commented key=value pair #foo=bar should be changed to foo=salt
  1434. The comment char (#) in front of foo=bar should be removed
  1435. '''
  1436. test_name = 'test_replace_issue_18612_change_mid_line_with_comment'
  1437. path_in = os.path.join(
  1438. FILES, 'file.replace', '{0}.in'.format(test_name)
  1439. )
  1440. path_out = os.path.join(
  1441. FILES, 'file.replace', '{0}.out'.format(test_name)
  1442. )
  1443. path_test = os.path.join(base_dir, test_name)
  1444. # create test file based on initial template
  1445. shutil.copyfile(path_in, path_test)
  1446. ret = []
  1447. for x in range(0, 3):
  1448. ret.append(self.run_state('file.replace',
  1449. name=path_test, pattern='^#foo=bar($|(?=\r\n))', repl='foo=salt', append_if_not_found=True))
  1450. # ensure, the resulting file contains the expected lines
  1451. self.assertTrue(filecmp.cmp(path_test, path_out))
  1452. # ensure the initial file was properly backed up
  1453. self.assertTrue(filecmp.cmp(path_test + '.bak', path_in))
  1454. # ensure, all 'file.replace' runs reported success
  1455. for item in ret:
  1456. self.assertSaltTrueReturn(item)
  1457. @with_tempdir()
  1458. def test_replace_issue_18841_no_changes(self, base_dir):
  1459. '''
  1460. Test the (mis-)behaviour of file.replace as described in #18841:
  1461. Using file.replace in a way which shouldn't modify the file at all
  1462. results in changed mtime of the original file and a backup file being created.
  1463. # Case description
  1464. The tested file contains multiple lines
  1465. The tested file contains a line already matching the replacement (no change needed)
  1466. The tested file's content shouldn't change at all
  1467. The tested file's mtime shouldn't change at all
  1468. No backup file should be created
  1469. '''
  1470. test_name = 'test_replace_issue_18841_no_changes'
  1471. path_in = os.path.join(
  1472. FILES, 'file.replace', '{0}.in'.format(test_name)
  1473. )
  1474. path_test = os.path.join(base_dir, test_name)
  1475. # create test file based on initial template
  1476. shutil.copyfile(path_in, path_test)
  1477. # get (m|a)time of file
  1478. fstats_orig = os.stat(path_test)
  1479. # define how far we predate the file
  1480. age = 5*24*60*60
  1481. # set (m|a)time of file 5 days into the past
  1482. os.utime(path_test, (fstats_orig.st_mtime-age, fstats_orig.st_atime-age))
  1483. ret = self.run_state('file.replace',
  1484. name=path_test,
  1485. pattern='^hello world$',
  1486. repl='goodbye world',
  1487. show_changes=True,
  1488. flags=['IGNORECASE'],
  1489. backup=False
  1490. )
  1491. # get (m|a)time of file
  1492. fstats_post = os.stat(path_test)
  1493. # ensure, the file content didn't change
  1494. self.assertTrue(filecmp.cmp(path_in, path_test))
  1495. # ensure no backup file was created
  1496. self.assertFalse(os.path.exists(path_test + '.bak'))
  1497. # ensure the file's mtime didn't change
  1498. self.assertTrue(fstats_post.st_mtime, fstats_orig.st_mtime-age)
  1499. # ensure, all 'file.replace' runs reported success
  1500. self.assertSaltTrueReturn(ret)
  1501. def test_serialize(self):
  1502. '''
  1503. Test to ensure that file.serialize returns a data structure that's
  1504. both serialized and formatted properly
  1505. '''
  1506. path_test = os.path.join(TMP, 'test_serialize')
  1507. ret = self.run_state('file.serialize',
  1508. name=path_test,
  1509. dataset={'name': 'naive',
  1510. 'description': 'A basic test',
  1511. 'a_list': ['first_element', 'second_element'],
  1512. 'finally': 'the last item'},
  1513. formatter='json')
  1514. with salt.utils.files.fopen(path_test, 'rb') as fp_:
  1515. serialized_file = salt.utils.stringutils.to_unicode(fp_.read())
  1516. # The JSON serializer uses LF even on OSes where os.sep is CRLF.
  1517. expected_file = '\n'.join([
  1518. '{',
  1519. ' "a_list": [',
  1520. ' "first_element",',
  1521. ' "second_element"',
  1522. ' ],',
  1523. ' "description": "A basic test",',
  1524. ' "finally": "the last item",',
  1525. ' "name": "naive"',
  1526. '}',
  1527. '',
  1528. ])
  1529. self.assertEqual(serialized_file, expected_file)
  1530. @with_tempfile(create=False)
  1531. def test_serializer_deserializer_opts(self, name):
  1532. '''
  1533. Test the serializer_opts and deserializer_opts options
  1534. '''
  1535. data1 = {'foo': {'bar': '%(x)s'}}
  1536. data2 = {'foo': {'abc': 123}}
  1537. merged = {'foo': {'y': 'not_used', 'x': 'baz', 'abc': 123, 'bar': u'baz'}}
  1538. ret = self.run_state(
  1539. 'file.serialize',
  1540. name=name,
  1541. dataset=data1,
  1542. formatter='configparser',
  1543. deserializer_opts=[{'defaults': {'y': 'not_used'}}])
  1544. ret = ret[next(iter(ret))]
  1545. assert ret['result'], ret
  1546. # We should have warned about deserializer_opts being used when
  1547. # merge_if_exists was not set to True.
  1548. assert 'warnings' in ret
  1549. # Run with merge_if_exists, as well as serializer and deserializer opts
  1550. # deserializer opts will be used for string interpolation of the %(x)s
  1551. # that was written to the file with data1 (i.e. bar should become baz)
  1552. ret = self.run_state(
  1553. 'file.serialize',
  1554. name=name,
  1555. dataset=data2,
  1556. formatter='configparser',
  1557. merge_if_exists=True,
  1558. serializer_opts=[{'defaults': {'y': 'not_used'}}],
  1559. deserializer_opts=[{'defaults': {'x': 'baz'}}])
  1560. ret = ret[next(iter(ret))]
  1561. assert ret['result'], ret
  1562. with salt.utils.files.fopen(name) as fp_:
  1563. serialized_data = salt.serializers.configparser.deserialize(fp_)
  1564. # If this test fails, this debug logging will help tell us how the
  1565. # serialized data differs from what was serialized.
  1566. log.debug('serialized_data = %r', serialized_data)
  1567. log.debug('merged = %r', merged)
  1568. # serializing with a default of 'y' will add y = not_used into foo
  1569. assert serialized_data['foo']['y'] == merged['foo']['y']
  1570. # deserializing with default of x = baz will perform interpolation on %(x)s
  1571. # and bar will then = baz
  1572. assert serialized_data['foo']['bar'] == merged['foo']['bar']
  1573. @with_tempdir()
  1574. def test_replace_issue_18841_omit_backup(self, base_dir):
  1575. '''
  1576. Test the (mis-)behaviour of file.replace as described in #18841:
  1577. Using file.replace in a way which shouldn't modify the file at all
  1578. results in changed mtime of the original file and a backup file being created.
  1579. # Case description
  1580. The tested file contains multiple lines
  1581. The tested file contains a line already matching the replacement (no change needed)
  1582. The tested file's content shouldn't change at all
  1583. The tested file's mtime shouldn't change at all
  1584. No backup file should be created, although backup=False isn't explicitly defined
  1585. '''
  1586. test_name = 'test_replace_issue_18841_omit_backup'
  1587. path_in = os.path.join(
  1588. FILES, 'file.replace', '{0}.in'.format(test_name)
  1589. )
  1590. path_test = os.path.join(base_dir, test_name)
  1591. # create test file based on initial template
  1592. shutil.copyfile(path_in, path_test)
  1593. # get (m|a)time of file
  1594. fstats_orig = os.stat(path_test)
  1595. # define how far we predate the file
  1596. age = 5*24*60*60
  1597. # set (m|a)time of file 5 days into the past
  1598. os.utime(path_test, (fstats_orig.st_mtime-age, fstats_orig.st_atime-age))
  1599. ret = self.run_state('file.replace',
  1600. name=path_test,
  1601. pattern='^hello world$',
  1602. repl='goodbye world',
  1603. show_changes=True,
  1604. flags=['IGNORECASE']
  1605. )
  1606. # get (m|a)time of file
  1607. fstats_post = os.stat(path_test)
  1608. # ensure, the file content didn't change
  1609. self.assertTrue(filecmp.cmp(path_in, path_test))
  1610. # ensure no backup file was created
  1611. self.assertFalse(os.path.exists(path_test + '.bak'))
  1612. # ensure the file's mtime didn't change
  1613. self.assertTrue(fstats_post.st_mtime, fstats_orig.st_mtime-age)
  1614. # ensure, all 'file.replace' runs reported success
  1615. self.assertSaltTrueReturn(ret)
  1616. @with_tempfile()
  1617. def test_comment(self, name):
  1618. '''
  1619. file.comment
  1620. '''
  1621. # write a line to file
  1622. with salt.utils.files.fopen(name, 'w+') as fp_:
  1623. fp_.write('comment_me')
  1624. # Look for changes with test=True: return should be "None" at the first run
  1625. ret = self.run_state('file.comment', test=True, name=name, regex='^comment')
  1626. self.assertSaltNoneReturn(ret)
  1627. # comment once
  1628. ret = self.run_state('file.comment', name=name, regex='^comment')
  1629. # result is positive
  1630. self.assertSaltTrueReturn(ret)
  1631. # line is commented
  1632. with salt.utils.files.fopen(name, 'r') as fp_:
  1633. self.assertTrue(fp_.read().startswith('#comment'))
  1634. # comment twice
  1635. ret = self.run_state('file.comment', name=name, regex='^comment')
  1636. # result is still positive
  1637. self.assertSaltTrueReturn(ret)
  1638. # line is still commented
  1639. with salt.utils.files.fopen(name, 'r') as fp_:
  1640. self.assertTrue(fp_.read().startswith('#comment'))
  1641. # Test previously commented file returns "True" now and not "None" with test=True
  1642. ret = self.run_state('file.comment', test=True, name=name, regex='^comment')
  1643. self.assertSaltTrueReturn(ret)
  1644. @with_tempfile()
  1645. def test_test_comment(self, name):
  1646. '''
  1647. file.comment test interface
  1648. '''
  1649. with salt.utils.files.fopen(name, 'w+') as fp_:
  1650. fp_.write('comment_me')
  1651. ret = self.run_state(
  1652. 'file.comment', test=True, name=name, regex='.*comment.*',
  1653. )
  1654. with salt.utils.files.fopen(name, 'r') as fp_:
  1655. self.assertNotIn('#comment', fp_.read())
  1656. self.assertSaltNoneReturn(ret)
  1657. @with_tempfile()
  1658. def test_uncomment(self, name):
  1659. '''
  1660. file.uncomment
  1661. '''
  1662. with salt.utils.files.fopen(name, 'w+') as fp_:
  1663. fp_.write('#comment_me')
  1664. ret = self.run_state('file.uncomment', name=name, regex='^comment')
  1665. with salt.utils.files.fopen(name, 'r') as fp_:
  1666. self.assertNotIn('#comment', fp_.read())
  1667. self.assertSaltTrueReturn(ret)
  1668. @with_tempfile()
  1669. def test_test_uncomment(self, name):
  1670. '''
  1671. file.comment test interface
  1672. '''
  1673. with salt.utils.files.fopen(name, 'w+') as fp_:
  1674. fp_.write('#comment_me')
  1675. ret = self.run_state(
  1676. 'file.uncomment', test=True, name=name, regex='^comment.*'
  1677. )
  1678. with salt.utils.files.fopen(name, 'r') as fp_:
  1679. self.assertIn('#comment', fp_.read())
  1680. self.assertSaltNoneReturn(ret)
  1681. @with_tempfile()
  1682. def test_append(self, name):
  1683. '''
  1684. file.append
  1685. '''
  1686. with salt.utils.files.fopen(name, 'w+') as fp_:
  1687. fp_.write('#salty!')
  1688. ret = self.run_state('file.append', name=name, text='cheese')
  1689. with salt.utils.files.fopen(name, 'r') as fp_:
  1690. self.assertIn('cheese', fp_.read())
  1691. self.assertSaltTrueReturn(ret)
  1692. @with_tempfile()
  1693. def test_test_append(self, name):
  1694. '''
  1695. file.append test interface
  1696. '''
  1697. with salt.utils.files.fopen(name, 'w+') as fp_:
  1698. fp_.write('#salty!')
  1699. ret = self.run_state(
  1700. 'file.append', test=True, name=name, text='cheese'
  1701. )
  1702. with salt.utils.files.fopen(name, 'r') as fp_:
  1703. self.assertNotIn('cheese', fp_.read())
  1704. self.assertSaltNoneReturn(ret)
  1705. @with_tempdir()
  1706. def test_append_issue_1864_makedirs(self, base_dir):
  1707. '''
  1708. file.append but create directories if needed as an option, and create
  1709. the file if it doesn't exist
  1710. '''
  1711. fname = 'append_issue_1864_makedirs'
  1712. name = os.path.join(base_dir, fname)
  1713. # Non existing file get's touched
  1714. ret = self.run_state(
  1715. 'file.append', name=name, text='cheese', makedirs=True
  1716. )
  1717. self.assertSaltTrueReturn(ret)
  1718. # Nested directory and file get's touched
  1719. name = os.path.join(base_dir, 'issue_1864', fname)
  1720. ret = self.run_state(
  1721. 'file.append', name=name, text='cheese', makedirs=True
  1722. )
  1723. self.assertSaltTrueReturn(ret)
  1724. # Parent directory exists but file does not and makedirs is False
  1725. name = os.path.join(base_dir, 'issue_1864', fname + '2')
  1726. ret = self.run_state(
  1727. 'file.append', name=name, text='cheese'
  1728. )
  1729. self.assertSaltTrueReturn(ret)
  1730. self.assertTrue(os.path.isfile(name))
  1731. @with_tempdir()
  1732. def test_prepend_issue_27401_makedirs(self, base_dir):
  1733. '''
  1734. file.prepend but create directories if needed as an option, and create
  1735. the file if it doesn't exist
  1736. '''
  1737. fname = 'prepend_issue_27401'
  1738. name = os.path.join(base_dir, fname)
  1739. # Non existing file get's touched
  1740. ret = self.run_state(
  1741. 'file.prepend', name=name, text='cheese', makedirs=True
  1742. )
  1743. self.assertSaltTrueReturn(ret)
  1744. # Nested directory and file get's touched
  1745. name = os.path.join(base_dir, 'issue_27401', fname)
  1746. ret = self.run_state(
  1747. 'file.prepend', name=name, text='cheese', makedirs=True
  1748. )
  1749. self.assertSaltTrueReturn(ret)
  1750. # Parent directory exists but file does not and makedirs is False
  1751. name = os.path.join(base_dir, 'issue_27401', fname + '2')
  1752. ret = self.run_state(
  1753. 'file.prepend', name=name, text='cheese'
  1754. )
  1755. self.assertSaltTrueReturn(ret)
  1756. self.assertTrue(os.path.isfile(name))
  1757. @with_tempfile()
  1758. def test_touch(self, name):
  1759. '''
  1760. file.touch
  1761. '''
  1762. ret = self.run_state('file.touch', name=name)
  1763. self.assertTrue(os.path.isfile(name))
  1764. self.assertSaltTrueReturn(ret)
  1765. @with_tempfile(create=False)
  1766. def test_test_touch(self, name):
  1767. '''
  1768. file.touch test interface
  1769. '''
  1770. ret = self.run_state('file.touch', test=True, name=name)
  1771. self.assertFalse(os.path.isfile(name))
  1772. self.assertSaltNoneReturn(ret)
  1773. @with_tempdir()
  1774. def test_touch_directory(self, base_dir):
  1775. '''
  1776. file.touch a directory
  1777. '''
  1778. name = os.path.join(base_dir, 'touch_test_dir')
  1779. os.mkdir(name)
  1780. ret = self.run_state('file.touch', name=name)
  1781. self.assertSaltTrueReturn(ret)
  1782. self.assertTrue(os.path.isdir(name))
  1783. @with_tempdir()
  1784. def test_issue_2227_file_append(self, base_dir):
  1785. '''
  1786. Text to append includes a percent symbol
  1787. '''
  1788. # let's make use of existing state to create a file with contents to
  1789. # test against
  1790. tmp_file_append = os.path.join(base_dir, 'test.append')
  1791. self.run_state('file.touch', name=tmp_file_append)
  1792. self.run_state(
  1793. 'file.append',
  1794. name=tmp_file_append,
  1795. source='salt://testappend/firstif')
  1796. self.run_state(
  1797. 'file.append',
  1798. name=tmp_file_append,
  1799. source='salt://testappend/secondif')
  1800. # Now our real test
  1801. try:
  1802. ret = self.run_state(
  1803. 'file.append',
  1804. name=tmp_file_append,
  1805. text="HISTTIMEFORMAT='%F %T '")
  1806. self.assertSaltTrueReturn(ret)
  1807. with salt.utils.files.fopen(tmp_file_append, 'r') as fp_:
  1808. contents_pre = fp_.read()
  1809. # It should not append text again
  1810. ret = self.run_state(
  1811. 'file.append',
  1812. name=tmp_file_append,
  1813. text="HISTTIMEFORMAT='%F %T '")
  1814. self.assertSaltTrueReturn(ret)
  1815. with salt.utils.files.fopen(tmp_file_append, 'r') as fp_:
  1816. contents_post = fp_.read()
  1817. self.assertEqual(contents_pre, contents_post)
  1818. except AssertionError:
  1819. if os.path.exists(tmp_file_append):
  1820. shutil.copy(tmp_file_append, tmp_file_append + '.bak')
  1821. raise
  1822. @with_tempdir()
  1823. def test_issue_2401_file_comment(self, base_dir):
  1824. # Get a path to the temporary file
  1825. tmp_file = os.path.join(base_dir, 'issue-2041-comment.txt')
  1826. # Write some data to it
  1827. with salt.utils.files.fopen(tmp_file, 'w') as fp_:
  1828. fp_.write('hello\nworld\n')
  1829. # create the sls template
  1830. template_lines = [
  1831. '{0}:'.format(tmp_file),
  1832. ' file.comment:',
  1833. ' - regex: ^world'
  1834. ]
  1835. template = '\n'.join(template_lines)
  1836. try:
  1837. ret = self.run_function(
  1838. 'state.template_str', [template], timeout=120
  1839. )
  1840. self.assertSaltTrueReturn(ret)
  1841. self.assertNotInSaltComment('Pattern already commented', ret)
  1842. self.assertInSaltComment('Commented lines successfully', ret)
  1843. # This next time, it is already commented.
  1844. ret = self.run_function(
  1845. 'state.template_str', [template], timeout=120
  1846. )
  1847. self.assertSaltTrueReturn(ret)
  1848. self.assertInSaltComment('Pattern already commented', ret)
  1849. except AssertionError:
  1850. shutil.copy(tmp_file, tmp_file + '.bak')
  1851. raise
  1852. @with_tempdir()
  1853. def test_issue_2379_file_append(self, base_dir):
  1854. # Get a path to the temporary file
  1855. tmp_file = os.path.join(base_dir, 'issue-2379-file-append.txt')
  1856. # Write some data to it
  1857. with salt.utils.files.fopen(tmp_file, 'w') as fp_:
  1858. fp_.write(
  1859. 'hello\nworld\n' # Some junk
  1860. '#PermitRootLogin yes\n' # Commented text
  1861. '# PermitRootLogin yes\n' # Commented text with space
  1862. )
  1863. # create the sls template
  1864. template_lines = [
  1865. '{0}:'.format(tmp_file),
  1866. ' file.append:',
  1867. ' - text: PermitRootLogin yes'
  1868. ]
  1869. template = '\n'.join(template_lines)
  1870. try:
  1871. ret = self.run_function('state.template_str', [template])
  1872. self.assertSaltTrueReturn(ret)
  1873. self.assertInSaltComment('Appended 1 lines', ret)
  1874. except AssertionError:
  1875. shutil.copy(tmp_file, tmp_file + '.bak')
  1876. raise
  1877. @skipIf(IS_WINDOWS, 'Mode not available in Windows')
  1878. @with_tempdir(create=False)
  1879. @with_tempdir(create=False)
  1880. def test_issue_2726_mode_kwarg(self, dir1, dir2):
  1881. # Let's test for the wrong usage approach
  1882. bad_mode_kwarg_testfile = os.path.join(
  1883. dir1, 'bad_mode_kwarg', 'testfile'
  1884. )
  1885. bad_template = [
  1886. '{0}:'.format(bad_mode_kwarg_testfile),
  1887. ' file.recurse:',
  1888. ' - source: salt://testfile',
  1889. ' - mode: 644'
  1890. ]
  1891. ret = self.run_function(
  1892. 'state.template_str', [os.linesep.join(bad_template)]
  1893. )
  1894. self.assertSaltFalseReturn(ret)
  1895. self.assertInSaltComment(
  1896. '\'mode\' is not allowed in \'file.recurse\'. Please use '
  1897. '\'file_mode\' and \'dir_mode\'.',
  1898. ret
  1899. )
  1900. self.assertNotInSaltComment(
  1901. 'TypeError: managed() got multiple values for keyword '
  1902. 'argument \'mode\'',
  1903. ret
  1904. )
  1905. # Now, the correct usage approach
  1906. good_mode_kwargs_testfile = os.path.join(
  1907. dir2, 'good_mode_kwargs', 'testappend'
  1908. )
  1909. good_template = [
  1910. '{0}:'.format(good_mode_kwargs_testfile),
  1911. ' file.recurse:',
  1912. ' - source: salt://testappend',
  1913. ' - dir_mode: 744',
  1914. ' - file_mode: 644',
  1915. ]
  1916. ret = self.run_function(
  1917. 'state.template_str', [os.linesep.join(good_template)]
  1918. )
  1919. self.assertSaltTrueReturn(ret)
  1920. @with_tempdir()
  1921. def test_issue_8343_accumulated_require_in(self, base_dir):
  1922. template_path = os.path.join(TMP_STATE_TREE, 'issue-8343.sls')
  1923. testcase_filedest = os.path.join(base_dir, 'issue-8343.txt')
  1924. if os.path.exists(template_path):
  1925. os.remove(template_path)
  1926. if os.path.exists(testcase_filedest):
  1927. os.remove(testcase_filedest)
  1928. sls_template = [
  1929. '{0}:',
  1930. ' file.managed:',
  1931. ' - contents: |',
  1932. ' #',
  1933. '',
  1934. 'prepend-foo-accumulator-from-pillar:',
  1935. ' file.accumulated:',
  1936. ' - require_in:',
  1937. ' - file: prepend-foo-management',
  1938. ' - filename: {0}',
  1939. ' - text: |',
  1940. ' foo',
  1941. '',
  1942. 'append-foo-accumulator-from-pillar:',
  1943. ' file.accumulated:',
  1944. ' - require_in:',
  1945. ' - file: append-foo-management',
  1946. ' - filename: {0}',
  1947. ' - text: |',
  1948. ' bar',
  1949. '',
  1950. 'prepend-foo-management:',
  1951. ' file.blockreplace:',
  1952. ' - name: {0}',
  1953. ' - marker_start: "#-- start salt managed zonestart -- PLEASE, DO NOT EDIT"',
  1954. ' - marker_end: "#-- end salt managed zonestart --"',
  1955. " - content: ''",
  1956. ' - prepend_if_not_found: True',
  1957. " - backup: '.bak'",
  1958. ' - show_changes: True',
  1959. '',
  1960. 'append-foo-management:',
  1961. ' file.blockreplace:',
  1962. ' - name: {0}',
  1963. ' - marker_start: "#-- start salt managed zoneend -- PLEASE, DO NOT EDIT"',
  1964. ' - marker_end: "#-- end salt managed zoneend --"',
  1965. " - content: ''",
  1966. ' - append_if_not_found: True',
  1967. " - backup: '.bak2'",
  1968. ' - show_changes: True',
  1969. '']
  1970. with salt.utils.files.fopen(template_path, 'w') as fp_:
  1971. fp_.write(
  1972. os.linesep.join(sls_template).format(testcase_filedest))
  1973. ret = self.run_function('state.sls', mods='issue-8343')
  1974. for name, step in six.iteritems(ret):
  1975. self.assertSaltTrueReturn({name: step})
  1976. with salt.utils.files.fopen(testcase_filedest) as fp_:
  1977. contents = fp_.read().split(os.linesep)
  1978. expected = [
  1979. '#-- start salt managed zonestart -- PLEASE, DO NOT EDIT',
  1980. 'foo',
  1981. '#-- end salt managed zonestart --',
  1982. '#',
  1983. '#-- start salt managed zoneend -- PLEASE, DO NOT EDIT',
  1984. 'bar',
  1985. '#-- end salt managed zoneend --',
  1986. '']
  1987. self.assertEqual([salt.utils.stringutils.to_str(line) for line in expected], contents)
  1988. @with_tempdir()
  1989. @skipIf(salt.utils.platform.is_darwin() and six.PY2, 'This test hangs on OS X on Py2')
  1990. def test_issue_11003_immutable_lazy_proxy_sum(self, base_dir):
  1991. # causes the Import-Module ServerManager error on Windows
  1992. template_path = os.path.join(TMP_STATE_TREE, 'issue-11003.sls')
  1993. testcase_filedest = os.path.join(base_dir, 'issue-11003.txt')
  1994. sls_template = [
  1995. 'a{0}:',
  1996. ' file.absent:',
  1997. ' - name: {0}',
  1998. '',
  1999. '{0}:',
  2000. ' file.managed:',
  2001. ' - contents: |',
  2002. ' #',
  2003. '',
  2004. 'test-acc1:',
  2005. ' file.accumulated:',
  2006. ' - require_in:',
  2007. ' - file: final',
  2008. ' - filename: {0}',
  2009. ' - text: |',
  2010. ' bar',
  2011. '',
  2012. 'test-acc2:',
  2013. ' file.accumulated:',
  2014. ' - watch_in:',
  2015. ' - file: final',
  2016. ' - filename: {0}',
  2017. ' - text: |',
  2018. ' baz',
  2019. '',
  2020. 'final:',
  2021. ' file.blockreplace:',
  2022. ' - name: {0}',
  2023. ' - marker_start: "#-- start managed zone PLEASE, DO NOT EDIT"',
  2024. ' - marker_end: "#-- end managed zone"',
  2025. ' - content: \'\'',
  2026. ' - append_if_not_found: True',
  2027. ' - show_changes: True'
  2028. ]
  2029. with salt.utils.files.fopen(template_path, 'w') as fp_:
  2030. fp_.write(os.linesep.join(sls_template).format(testcase_filedest))
  2031. ret = self.run_function('state.sls', mods='issue-11003', timeout=600)
  2032. for name, step in six.iteritems(ret):
  2033. self.assertSaltTrueReturn({name: step})
  2034. with salt.utils.files.fopen(testcase_filedest) as fp_:
  2035. contents = fp_.read().split(os.linesep)
  2036. begin = contents.index(
  2037. '#-- start managed zone PLEASE, DO NOT EDIT') + 1
  2038. end = contents.index('#-- end managed zone')
  2039. block_contents = contents[begin:end]
  2040. for item in ('', 'bar', 'baz'):
  2041. block_contents.remove(item)
  2042. self.assertEqual(block_contents, [])
  2043. @with_tempdir()
  2044. def test_issue_8947_utf8_sls(self, base_dir):
  2045. '''
  2046. Test some file operation with utf-8 characters on the sls
  2047. This is more generic than just a file test. Feel free to move
  2048. '''
  2049. self.maxDiff = None
  2050. korean_1 = '한국어 시험'
  2051. korean_2 = '첫 번째 행'
  2052. korean_3 = '마지막 행'
  2053. test_file = os.path.join(base_dir, '{0}.txt'.format(korean_1))
  2054. test_file_encoded = test_file
  2055. template_path = os.path.join(TMP_STATE_TREE, 'issue-8947.sls')
  2056. # create the sls template
  2057. template = textwrap.dedent('''\
  2058. some-utf8-file-create:
  2059. file.managed:
  2060. - name: {test_file}
  2061. - contents: {korean_1}
  2062. - makedirs: True
  2063. - replace: True
  2064. - show_diff: True
  2065. some-utf8-file-create2:
  2066. file.managed:
  2067. - name: {test_file}
  2068. - contents: |
  2069. {korean_2}
  2070. {korean_1}
  2071. {korean_3}
  2072. - replace: True
  2073. - show_diff: True
  2074. '''.format(**locals()))
  2075. if not salt.utils.platform.is_windows():
  2076. template += textwrap.dedent('''\
  2077. some-utf8-file-content-test:
  2078. cmd.run:
  2079. - name: 'cat "{test_file}"'
  2080. - require:
  2081. - file: some-utf8-file-create2
  2082. '''.format(**locals()))
  2083. # Save template file
  2084. with salt.utils.files.fopen(template_path, 'wb') as fp_:
  2085. fp_.write(salt.utils.stringutils.to_bytes(template))
  2086. try:
  2087. result = self.run_function('state.sls', mods='issue-8947')
  2088. if not isinstance(result, dict):
  2089. raise AssertionError(
  2090. ('Something went really wrong while testing this sls:'
  2091. ' {0}').format(repr(result))
  2092. )
  2093. # difflib produces different output on python 2.6 than on >=2.7
  2094. if sys.version_info < (2, 7):
  2095. diff = '--- \n+++ \n@@ -1,1 +1,3 @@\n'
  2096. else:
  2097. diff = '--- \n+++ \n@@ -1 +1,3 @@\n'
  2098. diff += (
  2099. '+첫 번째 행{0}'
  2100. ' 한국어 시험{0}'
  2101. '+마지막 행{0}'
  2102. ).format(os.linesep)
  2103. ret = {x.split('_|-')[1]: y for x, y in six.iteritems(result)}
  2104. # Confirm initial creation of file
  2105. self.assertEqual(
  2106. ret['some-utf8-file-create']['comment'],
  2107. 'File {0} updated'.format(test_file_encoded)
  2108. )
  2109. self.assertEqual(
  2110. ret['some-utf8-file-create']['changes'],
  2111. {'diff': 'New file'}
  2112. )
  2113. # Confirm file was modified and that the diff was as expected
  2114. self.assertEqual(
  2115. ret['some-utf8-file-create2']['comment'],
  2116. 'File {0} updated'.format(test_file_encoded)
  2117. )
  2118. self.assertEqual(
  2119. ret['some-utf8-file-create2']['changes'],
  2120. {'diff': diff}
  2121. )
  2122. if salt.utils.platform.is_windows():
  2123. import subprocess
  2124. import win32api
  2125. p = subprocess.Popen(
  2126. salt.utils.stringutils.to_str(
  2127. 'type {}'.format(win32api.GetShortPathName(test_file))),
  2128. shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  2129. p.poll()
  2130. out = p.stdout.read()
  2131. self.assertEqual(
  2132. out.decode('utf-8'),
  2133. os.linesep.join((korean_2, korean_1, korean_3)) + os.linesep
  2134. )
  2135. else:
  2136. self.assertEqual(
  2137. ret['some-utf8-file-content-test']['comment'],
  2138. 'Command "cat "{0}"" run'.format(
  2139. test_file_encoded
  2140. )
  2141. )
  2142. self.assertEqual(
  2143. ret['some-utf8-file-content-test']['changes']['stdout'],
  2144. '\n'.join((korean_2, korean_1, korean_3))
  2145. )
  2146. finally:
  2147. try:
  2148. os.remove(template_path)
  2149. except OSError:
  2150. pass
  2151. @skip_if_not_root
  2152. @skipIf(not HAS_PWD, "pwd not available. Skipping test")
  2153. @skipIf(not HAS_GRP, "grp not available. Skipping test")
  2154. @with_system_user_and_group(TEST_SYSTEM_USER, TEST_SYSTEM_GROUP,
  2155. on_existing='delete', delete=True)
  2156. @with_tempdir()
  2157. def test_issue_12209_follow_symlinks(self, tempdir, user, group):
  2158. '''
  2159. Ensure that symlinks are properly chowned when recursing (following
  2160. symlinks)
  2161. '''
  2162. # Make the directories for this test
  2163. onedir = os.path.join(tempdir, 'one')
  2164. twodir = os.path.join(tempdir, 'two')
  2165. os.mkdir(onedir)
  2166. os.symlink(onedir, twodir)
  2167. # Run the state
  2168. ret = self.run_state(
  2169. 'file.directory', name=tempdir, follow_symlinks=True,
  2170. user=user, group=group, recurse=['user', 'group']
  2171. )
  2172. self.assertSaltTrueReturn(ret)
  2173. # Double-check, in case state mis-reported a True result. Since we are
  2174. # following symlinks, we expect twodir to still be owned by root, but
  2175. # onedir should be owned by the 'issue12209' user.
  2176. onestats = os.stat(onedir)
  2177. twostats = os.lstat(twodir)
  2178. self.assertEqual(pwd.getpwuid(onestats.st_uid).pw_name, user)
  2179. self.assertEqual(pwd.getpwuid(twostats.st_uid).pw_name, 'root')
  2180. self.assertEqual(grp.getgrgid(onestats.st_gid).gr_name, group)
  2181. if salt.utils.path.which('id'):
  2182. root_group = self.run_function('user.primary_group', ['root'])
  2183. self.assertEqual(grp.getgrgid(twostats.st_gid).gr_name, root_group)
  2184. @skip_if_not_root
  2185. @skipIf(not HAS_PWD, "pwd not available. Skipping test")
  2186. @skipIf(not HAS_GRP, "grp not available. Skipping test")
  2187. @with_system_user_and_group(TEST_SYSTEM_USER, TEST_SYSTEM_GROUP,
  2188. on_existing='delete', delete=True)
  2189. @with_tempdir()
  2190. def test_issue_12209_no_follow_symlinks(self, tempdir, user, group):
  2191. '''
  2192. Ensure that symlinks are properly chowned when recursing (not following
  2193. symlinks)
  2194. '''
  2195. # Make the directories for this test
  2196. onedir = os.path.join(tempdir, 'one')
  2197. twodir = os.path.join(tempdir, 'two')
  2198. os.mkdir(onedir)
  2199. os.symlink(onedir, twodir)
  2200. # Run the state
  2201. ret = self.run_state(
  2202. 'file.directory', name=tempdir, follow_symlinks=False,
  2203. user=user, group=group, recurse=['user', 'group']
  2204. )
  2205. self.assertSaltTrueReturn(ret)
  2206. # Double-check, in case state mis-reported a True result. Since we
  2207. # are not following symlinks, we expect twodir to now be owned by
  2208. # the 'issue12209' user, just link onedir.
  2209. onestats = os.stat(onedir)
  2210. twostats = os.lstat(twodir)
  2211. self.assertEqual(pwd.getpwuid(onestats.st_uid).pw_name, user)
  2212. self.assertEqual(pwd.getpwuid(twostats.st_uid).pw_name, user)
  2213. self.assertEqual(grp.getgrgid(onestats.st_gid).gr_name, group)
  2214. self.assertEqual(grp.getgrgid(twostats.st_gid).gr_name, group)
  2215. @with_tempfile(create=False)
  2216. @with_tempfile()
  2217. def test_template_local_file(self, source, dest):
  2218. '''
  2219. Test a file.managed state with a local file as the source. Test both
  2220. with the file:// protocol designation prepended, and without it.
  2221. '''
  2222. with salt.utils.files.fopen(source, 'w') as fp_:
  2223. fp_.write('{{ foo }}\n')
  2224. for prefix in ('file://', ''):
  2225. ret = self.run_state(
  2226. 'file.managed',
  2227. name=dest,
  2228. source=prefix + source,
  2229. template='jinja',
  2230. context={'foo': 'Hello world!'}
  2231. )
  2232. self.assertSaltTrueReturn(ret)
  2233. @with_tempfile()
  2234. def test_template_local_file_noclobber(self, source):
  2235. '''
  2236. Test the case where a source file is in the minion's local filesystem,
  2237. and the source path is the same as the destination path.
  2238. '''
  2239. with salt.utils.files.fopen(source, 'w') as fp_:
  2240. fp_.write('{{ foo }}\n')
  2241. ret = self.run_state(
  2242. 'file.managed',
  2243. name=source,
  2244. source=source,
  2245. template='jinja',
  2246. context={'foo': 'Hello world!'}
  2247. )
  2248. self.assertSaltFalseReturn(ret)
  2249. self.assertIn(
  2250. ('Source file cannot be the same as destination'),
  2251. ret[next(iter(ret))]['comment'],
  2252. )
  2253. @with_tempfile(create=False)
  2254. @with_tempfile(create=False)
  2255. def test_issue_25250_force_copy_deletes(self, source, dest):
  2256. '''
  2257. ensure force option in copy state does not delete target file
  2258. '''
  2259. shutil.copyfile(os.path.join(FILES, 'hosts'), source)
  2260. shutil.copyfile(os.path.join(FILES, 'file/base/cheese'), dest)
  2261. self.run_state('file.copy', name=dest, source=source, force=True)
  2262. self.assertTrue(os.path.exists(dest))
  2263. self.assertTrue(filecmp.cmp(source, dest))
  2264. os.remove(source)
  2265. os.remove(dest)
  2266. @destructiveTest
  2267. @with_tempfile()
  2268. def test_file_copy_make_dirs(self, source):
  2269. '''
  2270. ensure make_dirs creates correct user perms
  2271. '''
  2272. shutil.copyfile(os.path.join(FILES, 'hosts'), source)
  2273. dest = os.path.join(TMP, 'dir1', 'dir2', 'copied_file.txt')
  2274. user = 'salt'
  2275. mode = '0644'
  2276. ret = self.run_function('user.add', [user])
  2277. self.assertTrue(ret, 'Failed to add user. Are you running as sudo?')
  2278. ret = self.run_state('file.copy', name=dest, source=source, user=user,
  2279. makedirs=True, mode=mode)
  2280. self.assertSaltTrueReturn(ret)
  2281. file_checks = [dest, os.path.join(TMP, 'dir1'), os.path.join(TMP, 'dir1', 'dir2')]
  2282. for check in file_checks:
  2283. user_check = self.run_function('file.get_user', [check])
  2284. mode_check = self.run_function('file.get_mode', [check])
  2285. self.assertEqual(user_check, user)
  2286. self.assertEqual(salt.utils.files.normalize_mode(mode_check), mode)
  2287. def test_contents_pillar_with_pillar_list(self):
  2288. '''
  2289. This tests for any regressions for this issue:
  2290. https://github.com/saltstack/salt/issues/30934
  2291. '''
  2292. state_file = 'file_contents_pillar'
  2293. ret = self.run_function('state.sls', mods=state_file)
  2294. self.assertSaltTrueReturn(ret)
  2295. @skip_if_not_root
  2296. @skipIf(not HAS_PWD, "pwd not available. Skipping test")
  2297. @skipIf(not HAS_GRP, "grp not available. Skipping test")
  2298. @with_system_user_and_group(TEST_SYSTEM_USER, TEST_SYSTEM_GROUP,
  2299. on_existing='delete', delete=True)
  2300. def test_owner_after_setuid(self, user, group):
  2301. '''
  2302. Test to check file user/group after setting setuid or setgid.
  2303. Because Python os.chown() does reset the setuid/setgid to 0.
  2304. https://github.com/saltstack/salt/pull/45257
  2305. '''
  2306. # Desired configuration.
  2307. desired = {
  2308. 'file': os.path.join(TMP, 'file_with_setuid'),
  2309. 'user': user,
  2310. 'group': group,
  2311. 'mode': '4750'
  2312. }
  2313. # Run the state.
  2314. ret = self.run_state(
  2315. 'file.managed', name=desired['file'],
  2316. user=desired['user'], group=desired['group'], mode=desired['mode']
  2317. )
  2318. # Check result.
  2319. file_stat = os.stat(desired['file'])
  2320. result = {
  2321. 'user': pwd.getpwuid(file_stat.st_uid).pw_name,
  2322. 'group': grp.getgrgid(file_stat.st_gid).gr_name,
  2323. 'mode': oct(stat.S_IMODE(file_stat.st_mode))
  2324. }
  2325. self.assertSaltTrueReturn(ret)
  2326. self.assertEqual(desired['user'], result['user'])
  2327. self.assertEqual(desired['group'], result['group'])
  2328. self.assertEqual(desired['mode'], result['mode'].lstrip('0Oo'))
  2329. def test_binary_contents(self):
  2330. '''
  2331. This tests to ensure that binary contents do not cause a traceback.
  2332. '''
  2333. name = os.path.join(TMP, '1px.gif')
  2334. try:
  2335. ret = self.run_state(
  2336. 'file.managed',
  2337. name=name,
  2338. contents=BINARY_FILE)
  2339. self.assertSaltTrueReturn(ret)
  2340. finally:
  2341. try:
  2342. os.remove(name)
  2343. except OSError:
  2344. pass
  2345. @skip_if_not_root
  2346. @skipIf(not HAS_PWD, "pwd not available. Skipping test")
  2347. @skipIf(not HAS_GRP, "grp not available. Skipping test")
  2348. @with_system_user_and_group(TEST_SYSTEM_USER, TEST_SYSTEM_GROUP,
  2349. on_existing='delete', delete=True)
  2350. @with_tempdir()
  2351. def test_issue_48336_file_managed_mode_setuid(self, tempdir, user, group):
  2352. '''
  2353. Ensure that mode is correct with changing of ownership and group
  2354. symlinks)
  2355. '''
  2356. tempfile = os.path.join(tempdir, 'temp_file_issue_48336')
  2357. # Run the state
  2358. ret = self.run_state(
  2359. 'file.managed', name=tempfile,
  2360. user=user, group=group, mode='4750',
  2361. )
  2362. self.assertSaltTrueReturn(ret)
  2363. # Check that the owner and group are correct, and
  2364. # the mode is what we expect
  2365. temp_file_stats = os.stat(tempfile)
  2366. # Normalize the mode
  2367. temp_file_mode = six.text_type(oct(stat.S_IMODE(temp_file_stats.st_mode)))
  2368. temp_file_mode = salt.utils.files.normalize_mode(temp_file_mode)
  2369. self.assertEqual(temp_file_mode, '4750')
  2370. self.assertEqual(pwd.getpwuid(temp_file_stats.st_uid).pw_name, user)
  2371. self.assertEqual(grp.getgrgid(temp_file_stats.st_gid).gr_name, group)
  2372. @with_tempdir()
  2373. def test_issue_48557(self, tempdir):
  2374. tempfile = os.path.join(tempdir, 'temp_file_issue_48557')
  2375. with salt.utils.files.fopen(tempfile, 'wb') as fp:
  2376. fp.write(os.linesep.join([
  2377. 'test1',
  2378. 'test2',
  2379. 'test3',
  2380. '',
  2381. ]).encode('utf-8'))
  2382. ret = self.run_state('file.line',
  2383. name=tempfile,
  2384. after='test2',
  2385. mode='insert',
  2386. content='test4')
  2387. self.assertSaltTrueReturn(ret)
  2388. with salt.utils.files.fopen(tempfile, 'rb') as fp:
  2389. content = fp.read()
  2390. self.assertEqual(content, os.linesep.join([
  2391. 'test1',
  2392. 'test2',
  2393. 'test4',
  2394. 'test3',
  2395. '',
  2396. ]).encode('utf-8'))
  2397. @with_tempfile()
  2398. def test_issue_50221(self, name):
  2399. expected = 'abc{0}{0}{0}'.format(os.linesep)
  2400. ret = self.run_function(
  2401. 'pillar.get',
  2402. ['issue-50221']
  2403. )
  2404. assert ret == expected
  2405. ret = self.run_function(
  2406. 'state.apply',
  2407. ['issue-50221'],
  2408. pillar={
  2409. 'name': name
  2410. },
  2411. )
  2412. self.assertSaltTrueReturn(ret)
  2413. with salt.utils.files.fopen(name, 'r') as fp:
  2414. contents = fp.read()
  2415. assert contents == expected
  2416. def test_managed_file_issue_51208(self):
  2417. '''
  2418. Test to ensure we can handle a file with escaped double-quotes
  2419. '''
  2420. name = os.path.join(TMP, 'issue_51208.txt')
  2421. ret = self.run_state(
  2422. 'file.managed', name=name, source='salt://issue-51208/vimrc.stub'
  2423. )
  2424. src = os.path.join(BASE_FILES, 'issue-51208', 'vimrc.stub')
  2425. with salt.utils.files.fopen(src, 'r') as fp_:
  2426. master_data = fp_.read()
  2427. with salt.utils.files.fopen(name, 'r') as fp_:
  2428. minion_data = fp_.read()
  2429. self.assertEqual(master_data, minion_data)
  2430. self.assertSaltTrueReturn(ret)
  2431. class BlockreplaceTest(ModuleCase, SaltReturnAssertsMixin):
  2432. marker_start = '# start'
  2433. marker_end = '# end'
  2434. content = dedent(six.text_type('''\
  2435. Line 1 of block
  2436. Line 2 of block
  2437. '''))
  2438. without_block = dedent(six.text_type('''\
  2439. Hello world!
  2440. # comment here
  2441. '''))
  2442. with_non_matching_block = dedent(six.text_type('''\
  2443. Hello world!
  2444. # start
  2445. No match here
  2446. # end
  2447. # comment here
  2448. '''))
  2449. with_non_matching_block_and_marker_end_not_after_newline = dedent(six.text_type('''\
  2450. Hello world!
  2451. # start
  2452. No match here# end
  2453. # comment here
  2454. '''))
  2455. with_matching_block = dedent(six.text_type('''\
  2456. Hello world!
  2457. # start
  2458. Line 1 of block
  2459. Line 2 of block
  2460. # end
  2461. # comment here
  2462. '''))
  2463. with_matching_block_and_extra_newline = dedent(six.text_type('''\
  2464. Hello world!
  2465. # start
  2466. Line 1 of block
  2467. Line 2 of block
  2468. # end
  2469. # comment here
  2470. '''))
  2471. with_matching_block_and_marker_end_not_after_newline = dedent(six.text_type('''\
  2472. Hello world!
  2473. # start
  2474. Line 1 of block
  2475. Line 2 of block# end
  2476. # comment here
  2477. '''))
  2478. content_explicit_posix_newlines = ('Line 1 of block\n'
  2479. 'Line 2 of block\n')
  2480. content_explicit_windows_newlines = ('Line 1 of block\r\n'
  2481. 'Line 2 of block\r\n')
  2482. without_block_explicit_posix_newlines = ('Hello world!\n\n'
  2483. '# comment here\n')
  2484. without_block_explicit_windows_newlines = ('Hello world!\r\n\r\n'
  2485. '# comment here\r\n')
  2486. with_block_prepended_explicit_posix_newlines = ('# start\n'
  2487. 'Line 1 of block\n'
  2488. 'Line 2 of block\n'
  2489. '# end\n'
  2490. 'Hello world!\n\n'
  2491. '# comment here\n')
  2492. with_block_prepended_explicit_windows_newlines = ('# start\r\n'
  2493. 'Line 1 of block\r\n'
  2494. 'Line 2 of block\r\n'
  2495. '# end\r\n'
  2496. 'Hello world!\r\n\r\n'
  2497. '# comment here\r\n')
  2498. with_block_appended_explicit_posix_newlines = ('Hello world!\n\n'
  2499. '# comment here\n'
  2500. '# start\n'
  2501. 'Line 1 of block\n'
  2502. 'Line 2 of block\n'
  2503. '# end\n')
  2504. with_block_appended_explicit_windows_newlines = ('Hello world!\r\n\r\n'
  2505. '# comment here\r\n'
  2506. '# start\r\n'
  2507. 'Line 1 of block\r\n'
  2508. 'Line 2 of block\r\n'
  2509. '# end\r\n')
  2510. @staticmethod
  2511. def _write(dest, content):
  2512. with salt.utils.files.fopen(dest, 'wb') as fp_:
  2513. fp_.write(salt.utils.stringutils.to_bytes(content))
  2514. @staticmethod
  2515. def _read(src):
  2516. with salt.utils.files.fopen(src, 'rb') as fp_:
  2517. return salt.utils.stringutils.to_unicode(fp_.read())
  2518. @with_tempfile()
  2519. def test_prepend(self, name):
  2520. '''
  2521. Test blockreplace when prepend_if_not_found=True and block doesn't
  2522. exist in file.
  2523. '''
  2524. expected = self.marker_start + os.linesep + self.content + \
  2525. self.marker_end + os.linesep + self.without_block
  2526. # Pass 1: content ends in newline
  2527. self._write(name, self.without_block)
  2528. ret = self.run_state('file.blockreplace',
  2529. name=name,
  2530. content=self.content,
  2531. marker_start=self.marker_start,
  2532. marker_end=self.marker_end,
  2533. prepend_if_not_found=True)
  2534. self.assertSaltTrueReturn(ret)
  2535. self.assertTrue(ret[next(iter(ret))]['changes'])
  2536. self.assertEqual(self._read(name), expected)
  2537. # Pass 1a: Re-run state, no changes should be made
  2538. ret = self.run_state('file.blockreplace',
  2539. name=name,
  2540. content=self.content,
  2541. marker_start=self.marker_start,
  2542. marker_end=self.marker_end,
  2543. prepend_if_not_found=True)
  2544. self.assertSaltTrueReturn(ret)
  2545. self.assertFalse(ret[next(iter(ret))]['changes'])
  2546. self.assertEqual(self._read(name), expected)
  2547. # Pass 2: content does not end in newline
  2548. self._write(name, self.without_block)
  2549. ret = self.run_state('file.blockreplace',
  2550. name=name,
  2551. content=self.content.rstrip('\r\n'),
  2552. marker_start=self.marker_start,
  2553. marker_end=self.marker_end,
  2554. prepend_if_not_found=True)
  2555. self.assertSaltTrueReturn(ret)
  2556. self.assertTrue(ret[next(iter(ret))]['changes'])
  2557. self.assertEqual(self._read(name), expected)
  2558. # Pass 2a: Re-run state, no changes should be made
  2559. ret = self.run_state('file.blockreplace',
  2560. name=name,
  2561. content=self.content.rstrip('\r\n'),
  2562. marker_start=self.marker_start,
  2563. marker_end=self.marker_end,
  2564. prepend_if_not_found=True)
  2565. self.assertSaltTrueReturn(ret)
  2566. self.assertFalse(ret[next(iter(ret))]['changes'])
  2567. self.assertEqual(self._read(name), expected)
  2568. @with_tempfile()
  2569. def test_prepend_append_newline(self, name):
  2570. '''
  2571. Test blockreplace when prepend_if_not_found=True and block doesn't
  2572. exist in file. Test with append_newline explicitly set to True.
  2573. '''
  2574. # Pass 1: content ends in newline
  2575. expected = self.marker_start + os.linesep + self.content + \
  2576. os.linesep + self.marker_end + os.linesep + self.without_block
  2577. self._write(name, self.without_block)
  2578. ret = self.run_state('file.blockreplace',
  2579. name=name,
  2580. content=self.content,
  2581. marker_start=self.marker_start,
  2582. marker_end=self.marker_end,
  2583. prepend_if_not_found=True,
  2584. append_newline=True)
  2585. self.assertSaltTrueReturn(ret)
  2586. self.assertTrue(ret[next(iter(ret))]['changes'])
  2587. self.assertEqual(self._read(name), expected)
  2588. # Pass 1a: Re-run state, no changes should be made
  2589. ret = self.run_state('file.blockreplace',
  2590. name=name,
  2591. content=self.content,
  2592. marker_start=self.marker_start,
  2593. marker_end=self.marker_end,
  2594. prepend_if_not_found=True,
  2595. append_newline=True)
  2596. self.assertSaltTrueReturn(ret)
  2597. self.assertFalse(ret[next(iter(ret))]['changes'])
  2598. self.assertEqual(self._read(name), expected)
  2599. # Pass 2: content does not end in newline
  2600. expected = self.marker_start + os.linesep + self.content + \
  2601. self.marker_end + os.linesep + self.without_block
  2602. self._write(name, self.without_block)
  2603. ret = self.run_state('file.blockreplace',
  2604. name=name,
  2605. content=self.content.rstrip('\r\n'),
  2606. marker_start=self.marker_start,
  2607. marker_end=self.marker_end,
  2608. prepend_if_not_found=True,
  2609. append_newline=True)
  2610. self.assertSaltTrueReturn(ret)
  2611. self.assertTrue(ret[next(iter(ret))]['changes'])
  2612. self.assertEqual(self._read(name), expected)
  2613. # Pass 2a: Re-run state, no changes should be made
  2614. ret = self.run_state('file.blockreplace',
  2615. name=name,
  2616. content=self.content.rstrip('\r\n'),
  2617. marker_start=self.marker_start,
  2618. marker_end=self.marker_end,
  2619. prepend_if_not_found=True,
  2620. append_newline=True)
  2621. self.assertSaltTrueReturn(ret)
  2622. self.assertFalse(ret[next(iter(ret))]['changes'])
  2623. self.assertEqual(self._read(name), expected)
  2624. @with_tempfile()
  2625. def test_prepend_no_append_newline(self, name):
  2626. '''
  2627. Test blockreplace when prepend_if_not_found=True and block doesn't
  2628. exist in file. Test with append_newline explicitly set to False.
  2629. '''
  2630. # Pass 1: content ends in newline
  2631. expected = self.marker_start + os.linesep + self.content + \
  2632. self.marker_end + os.linesep + self.without_block
  2633. self._write(name, self.without_block)
  2634. ret = self.run_state('file.blockreplace',
  2635. name=name,
  2636. content=self.content,
  2637. marker_start=self.marker_start,
  2638. marker_end=self.marker_end,
  2639. prepend_if_not_found=True,
  2640. append_newline=False)
  2641. self.assertSaltTrueReturn(ret)
  2642. self.assertTrue(ret[next(iter(ret))]['changes'])
  2643. self.assertEqual(self._read(name), expected)
  2644. # Pass 1a: Re-run state, no changes should be made
  2645. ret = self.run_state('file.blockreplace',
  2646. name=name,
  2647. content=self.content,
  2648. marker_start=self.marker_start,
  2649. marker_end=self.marker_end,
  2650. prepend_if_not_found=True,
  2651. append_newline=False)
  2652. self.assertSaltTrueReturn(ret)
  2653. self.assertFalse(ret[next(iter(ret))]['changes'])
  2654. self.assertEqual(self._read(name), expected)
  2655. # Pass 2: content does not end in newline
  2656. expected = self.marker_start + os.linesep + \
  2657. self.content.rstrip('\r\n') + self.marker_end + os.linesep + \
  2658. self.without_block
  2659. self._write(name, self.without_block)
  2660. ret = self.run_state('file.blockreplace',
  2661. name=name,
  2662. content=self.content.rstrip('\r\n'),
  2663. marker_start=self.marker_start,
  2664. marker_end=self.marker_end,
  2665. prepend_if_not_found=True,
  2666. append_newline=False)
  2667. self.assertSaltTrueReturn(ret)
  2668. self.assertTrue(ret[next(iter(ret))]['changes'])
  2669. self.assertEqual(self._read(name), expected)
  2670. # Pass 2a: Re-run state, no changes should be made
  2671. ret = self.run_state('file.blockreplace',
  2672. name=name,
  2673. content=self.content.rstrip('\r\n'),
  2674. marker_start=self.marker_start,
  2675. marker_end=self.marker_end,
  2676. prepend_if_not_found=True,
  2677. append_newline=False)
  2678. self.assertSaltTrueReturn(ret)
  2679. self.assertFalse(ret[next(iter(ret))]['changes'])
  2680. self.assertEqual(self._read(name), expected)
  2681. @with_tempfile()
  2682. def test_append(self, name):
  2683. '''
  2684. Test blockreplace when append_if_not_found=True and block doesn't
  2685. exist in file.
  2686. '''
  2687. expected = self.without_block + self.marker_start + os.linesep + \
  2688. self.content + self.marker_end + os.linesep
  2689. # Pass 1: content ends in newline
  2690. self._write(name, self.without_block)
  2691. ret = self.run_state('file.blockreplace',
  2692. name=name,
  2693. content=self.content,
  2694. marker_start=self.marker_start,
  2695. marker_end=self.marker_end,
  2696. append_if_not_found=True)
  2697. self.assertSaltTrueReturn(ret)
  2698. self.assertTrue(ret[next(iter(ret))]['changes'])
  2699. self.assertEqual(self._read(name), expected)
  2700. # Pass 1a: Re-run state, no changes should be made
  2701. ret = self.run_state('file.blockreplace',
  2702. name=name,
  2703. content=self.content,
  2704. marker_start=self.marker_start,
  2705. marker_end=self.marker_end,
  2706. append_if_not_found=True)
  2707. self.assertSaltTrueReturn(ret)
  2708. self.assertFalse(ret[next(iter(ret))]['changes'])
  2709. self.assertEqual(self._read(name), expected)
  2710. # Pass 2: content does not end in newline
  2711. self._write(name, self.without_block)
  2712. ret = self.run_state('file.blockreplace',
  2713. name=name,
  2714. content=self.content.rstrip('\r\n'),
  2715. marker_start=self.marker_start,
  2716. marker_end=self.marker_end,
  2717. append_if_not_found=True)
  2718. self.assertSaltTrueReturn(ret)
  2719. self.assertTrue(ret[next(iter(ret))]['changes'])
  2720. self.assertEqual(self._read(name), expected)
  2721. # Pass 2a: Re-run state, no changes should be made
  2722. ret = self.run_state('file.blockreplace',
  2723. name=name,
  2724. content=self.content.rstrip('\r\n'),
  2725. marker_start=self.marker_start,
  2726. marker_end=self.marker_end,
  2727. append_if_not_found=True)
  2728. self.assertSaltTrueReturn(ret)
  2729. self.assertFalse(ret[next(iter(ret))]['changes'])
  2730. self.assertEqual(self._read(name), expected)
  2731. @with_tempfile()
  2732. def test_append_append_newline(self, name):
  2733. '''
  2734. Test blockreplace when append_if_not_found=True and block doesn't
  2735. exist in file. Test with append_newline explicitly set to True.
  2736. '''
  2737. # Pass 1: content ends in newline
  2738. expected = self.without_block + self.marker_start + os.linesep + \
  2739. self.content + os.linesep + self.marker_end + os.linesep
  2740. self._write(name, self.without_block)
  2741. ret = self.run_state('file.blockreplace',
  2742. name=name,
  2743. content=self.content,
  2744. marker_start=self.marker_start,
  2745. marker_end=self.marker_end,
  2746. append_if_not_found=True,
  2747. append_newline=True)
  2748. self.assertSaltTrueReturn(ret)
  2749. self.assertTrue(ret[next(iter(ret))]['changes'])
  2750. self.assertEqual(self._read(name), expected)
  2751. # Pass 1a: Re-run state, no changes should be made
  2752. ret = self.run_state('file.blockreplace',
  2753. name=name,
  2754. content=self.content,
  2755. marker_start=self.marker_start,
  2756. marker_end=self.marker_end,
  2757. append_if_not_found=True,
  2758. append_newline=True)
  2759. self.assertSaltTrueReturn(ret)
  2760. self.assertFalse(ret[next(iter(ret))]['changes'])
  2761. self.assertEqual(self._read(name), expected)
  2762. # Pass 2: content does not end in newline
  2763. expected = self.without_block + self.marker_start + os.linesep + \
  2764. self.content + self.marker_end + os.linesep
  2765. self._write(name, self.without_block)
  2766. ret = self.run_state('file.blockreplace',
  2767. name=name,
  2768. content=self.content.rstrip('\r\n'),
  2769. marker_start=self.marker_start,
  2770. marker_end=self.marker_end,
  2771. append_if_not_found=True,
  2772. append_newline=True)
  2773. self.assertSaltTrueReturn(ret)
  2774. self.assertTrue(ret[next(iter(ret))]['changes'])
  2775. self.assertEqual(self._read(name), expected)
  2776. # Pass 2a: Re-run state, no changes should be made
  2777. ret = self.run_state('file.blockreplace',
  2778. name=name,
  2779. content=self.content.rstrip('\r\n'),
  2780. marker_start=self.marker_start,
  2781. marker_end=self.marker_end,
  2782. append_if_not_found=True,
  2783. append_newline=True)
  2784. self.assertSaltTrueReturn(ret)
  2785. self.assertFalse(ret[next(iter(ret))]['changes'])
  2786. self.assertEqual(self._read(name), expected)
  2787. @with_tempfile()
  2788. def test_append_no_append_newline(self, name):
  2789. '''
  2790. Test blockreplace when append_if_not_found=True and block doesn't
  2791. exist in file. Test with append_newline explicitly set to False.
  2792. '''
  2793. # Pass 1: content ends in newline
  2794. expected = self.without_block + self.marker_start + os.linesep + \
  2795. self.content + self.marker_end + os.linesep
  2796. self._write(name, self.without_block)
  2797. ret = self.run_state('file.blockreplace',
  2798. name=name,
  2799. content=self.content,
  2800. marker_start=self.marker_start,
  2801. marker_end=self.marker_end,
  2802. append_if_not_found=True,
  2803. append_newline=False)
  2804. self.assertSaltTrueReturn(ret)
  2805. self.assertTrue(ret[next(iter(ret))]['changes'])
  2806. self.assertEqual(self._read(name), expected)
  2807. # Pass 1a: Re-run state, no changes should be made
  2808. ret = self.run_state('file.blockreplace',
  2809. name=name,
  2810. content=self.content,
  2811. marker_start=self.marker_start,
  2812. marker_end=self.marker_end,
  2813. append_if_not_found=True,
  2814. append_newline=False)
  2815. self.assertSaltTrueReturn(ret)
  2816. self.assertFalse(ret[next(iter(ret))]['changes'])
  2817. self.assertEqual(self._read(name), expected)
  2818. # Pass 2: content does not end in newline
  2819. expected = self.without_block + self.marker_start + os.linesep + \
  2820. self.content.rstrip('\r\n') + self.marker_end + os.linesep
  2821. self._write(name, self.without_block)
  2822. ret = self.run_state('file.blockreplace',
  2823. name=name,
  2824. content=self.content.rstrip('\r\n'),
  2825. marker_start=self.marker_start,
  2826. marker_end=self.marker_end,
  2827. append_if_not_found=True,
  2828. append_newline=False)
  2829. self.assertSaltTrueReturn(ret)
  2830. self.assertTrue(ret[next(iter(ret))]['changes'])
  2831. self.assertEqual(self._read(name), expected)
  2832. # Pass 2a: Re-run state, no changes should be made
  2833. ret = self.run_state('file.blockreplace',
  2834. name=name,
  2835. content=self.content.rstrip('\r\n'),
  2836. marker_start=self.marker_start,
  2837. marker_end=self.marker_end,
  2838. append_if_not_found=True,
  2839. append_newline=False)
  2840. self.assertSaltTrueReturn(ret)
  2841. self.assertFalse(ret[next(iter(ret))]['changes'])
  2842. self.assertEqual(self._read(name), expected)
  2843. @with_tempfile()
  2844. def test_prepend_auto_line_separator(self, name):
  2845. '''
  2846. This tests the line separator auto-detection when prepending the block
  2847. '''
  2848. # POSIX newlines to Windows newlines
  2849. self._write(name, self.without_block_explicit_windows_newlines)
  2850. ret = self.run_state('file.blockreplace',
  2851. name=name,
  2852. content=self.content_explicit_posix_newlines,
  2853. marker_start=self.marker_start,
  2854. marker_end=self.marker_end,
  2855. prepend_if_not_found=True)
  2856. self.assertSaltTrueReturn(ret)
  2857. self.assertTrue(ret[next(iter(ret))]['changes'])
  2858. self.assertEqual(
  2859. self._read(name),
  2860. self.with_block_prepended_explicit_windows_newlines)
  2861. # Re-run state, no changes should be made
  2862. ret = self.run_state('file.blockreplace',
  2863. name=name,
  2864. content=self.content_explicit_posix_newlines,
  2865. marker_start=self.marker_start,
  2866. marker_end=self.marker_end,
  2867. prepend_if_not_found=True)
  2868. self.assertSaltTrueReturn(ret)
  2869. self.assertFalse(ret[next(iter(ret))]['changes'])
  2870. self.assertEqual(
  2871. self._read(name),
  2872. self.with_block_prepended_explicit_windows_newlines)
  2873. # Windows newlines to POSIX newlines
  2874. self._write(name, self.without_block_explicit_posix_newlines)
  2875. ret = self.run_state('file.blockreplace',
  2876. name=name,
  2877. content=self.content_explicit_windows_newlines,
  2878. marker_start=self.marker_start,
  2879. marker_end=self.marker_end,
  2880. prepend_if_not_found=True)
  2881. self.assertSaltTrueReturn(ret)
  2882. self.assertTrue(ret[next(iter(ret))]['changes'])
  2883. self.assertEqual(
  2884. self._read(name),
  2885. self.with_block_prepended_explicit_posix_newlines)
  2886. # Re-run state, no changes should be made
  2887. ret = self.run_state('file.blockreplace',
  2888. name=name,
  2889. content=self.content_explicit_windows_newlines,
  2890. marker_start=self.marker_start,
  2891. marker_end=self.marker_end,
  2892. prepend_if_not_found=True)
  2893. self.assertSaltTrueReturn(ret)
  2894. self.assertFalse(ret[next(iter(ret))]['changes'])
  2895. self.assertEqual(
  2896. self._read(name),
  2897. self.with_block_prepended_explicit_posix_newlines)
  2898. @with_tempfile()
  2899. def test_append_auto_line_separator(self, name):
  2900. '''
  2901. This tests the line separator auto-detection when appending the block
  2902. '''
  2903. # POSIX newlines to Windows newlines
  2904. self._write(name, self.without_block_explicit_windows_newlines)
  2905. ret = self.run_state('file.blockreplace',
  2906. name=name,
  2907. content=self.content_explicit_posix_newlines,
  2908. marker_start=self.marker_start,
  2909. marker_end=self.marker_end,
  2910. append_if_not_found=True)
  2911. self.assertSaltTrueReturn(ret)
  2912. self.assertTrue(ret[next(iter(ret))]['changes'])
  2913. self.assertEqual(
  2914. self._read(name),
  2915. self.with_block_appended_explicit_windows_newlines)
  2916. # Re-run state, no changes should be made
  2917. ret = self.run_state('file.blockreplace',
  2918. name=name,
  2919. content=self.content_explicit_posix_newlines,
  2920. marker_start=self.marker_start,
  2921. marker_end=self.marker_end,
  2922. append_if_not_found=True)
  2923. self.assertSaltTrueReturn(ret)
  2924. self.assertFalse(ret[next(iter(ret))]['changes'])
  2925. self.assertEqual(
  2926. self._read(name),
  2927. self.with_block_appended_explicit_windows_newlines)
  2928. # Windows newlines to POSIX newlines
  2929. self._write(name, self.without_block_explicit_posix_newlines)
  2930. ret = self.run_state('file.blockreplace',
  2931. name=name,
  2932. content=self.content_explicit_windows_newlines,
  2933. marker_start=self.marker_start,
  2934. marker_end=self.marker_end,
  2935. append_if_not_found=True)
  2936. self.assertSaltTrueReturn(ret)
  2937. self.assertTrue(ret[next(iter(ret))]['changes'])
  2938. self.assertEqual(
  2939. self._read(name),
  2940. self.with_block_appended_explicit_posix_newlines)
  2941. # Re-run state, no changes should be made
  2942. ret = self.run_state('file.blockreplace',
  2943. name=name,
  2944. content=self.content_explicit_windows_newlines,
  2945. marker_start=self.marker_start,
  2946. marker_end=self.marker_end,
  2947. append_if_not_found=True)
  2948. self.assertSaltTrueReturn(ret)
  2949. self.assertFalse(ret[next(iter(ret))]['changes'])
  2950. self.assertEqual(
  2951. self._read(name),
  2952. self.with_block_appended_explicit_posix_newlines)
  2953. @with_tempfile()
  2954. def test_non_matching_block(self, name):
  2955. '''
  2956. Test blockreplace when block exists but its contents are not a
  2957. match.
  2958. '''
  2959. # Pass 1: content ends in newline
  2960. self._write(name, self.with_non_matching_block)
  2961. ret = self.run_state('file.blockreplace',
  2962. name=name,
  2963. content=self.content,
  2964. marker_start=self.marker_start,
  2965. marker_end=self.marker_end)
  2966. self.assertSaltTrueReturn(ret)
  2967. self.assertTrue(ret[next(iter(ret))]['changes'])
  2968. self.assertEqual(self._read(name), self.with_matching_block)
  2969. # Pass 1a: Re-run state, no changes should be made
  2970. ret = self.run_state('file.blockreplace',
  2971. name=name,
  2972. content=self.content,
  2973. marker_start=self.marker_start,
  2974. marker_end=self.marker_end)
  2975. self.assertSaltTrueReturn(ret)
  2976. self.assertFalse(ret[next(iter(ret))]['changes'])
  2977. self.assertEqual(self._read(name), self.with_matching_block)
  2978. # Pass 2: content does not end in newline
  2979. self._write(name, self.with_non_matching_block)
  2980. ret = self.run_state('file.blockreplace',
  2981. name=name,
  2982. content=self.content.rstrip('\r\n'),
  2983. marker_start=self.marker_start,
  2984. marker_end=self.marker_end)
  2985. self.assertSaltTrueReturn(ret)
  2986. self.assertTrue(ret[next(iter(ret))]['changes'])
  2987. self.assertEqual(self._read(name), self.with_matching_block)
  2988. # Pass 2a: Re-run state, no changes should be made
  2989. ret = self.run_state('file.blockreplace',
  2990. name=name,
  2991. content=self.content.rstrip('\r\n'),
  2992. marker_start=self.marker_start,
  2993. marker_end=self.marker_end)
  2994. self.assertSaltTrueReturn(ret)
  2995. self.assertFalse(ret[next(iter(ret))]['changes'])
  2996. self.assertEqual(self._read(name), self.with_matching_block)
  2997. @with_tempfile()
  2998. def test_non_matching_block_append_newline(self, name):
  2999. '''
  3000. Test blockreplace when block exists but its contents are not a
  3001. match. Test with append_newline explicitly set to True.
  3002. '''
  3003. # Pass 1: content ends in newline
  3004. self._write(name, self.with_non_matching_block)
  3005. ret = self.run_state('file.blockreplace',
  3006. name=name,
  3007. content=self.content,
  3008. marker_start=self.marker_start,
  3009. marker_end=self.marker_end,
  3010. append_newline=True)
  3011. self.assertSaltTrueReturn(ret)
  3012. self.assertTrue(ret[next(iter(ret))]['changes'])
  3013. self.assertEqual(
  3014. self._read(name),
  3015. self.with_matching_block_and_extra_newline)
  3016. # Pass 1a: Re-run state, no changes should be made
  3017. ret = self.run_state('file.blockreplace',
  3018. name=name,
  3019. content=self.content,
  3020. marker_start=self.marker_start,
  3021. marker_end=self.marker_end,
  3022. append_newline=True)
  3023. self.assertSaltTrueReturn(ret)
  3024. self.assertFalse(ret[next(iter(ret))]['changes'])
  3025. self.assertEqual(
  3026. self._read(name),
  3027. self.with_matching_block_and_extra_newline)
  3028. # Pass 2: content does not end in newline
  3029. self._write(name, self.with_non_matching_block)
  3030. ret = self.run_state('file.blockreplace',
  3031. name=name,
  3032. content=self.content.rstrip('\r\n'),
  3033. marker_start=self.marker_start,
  3034. marker_end=self.marker_end,
  3035. append_newline=True)
  3036. self.assertSaltTrueReturn(ret)
  3037. self.assertTrue(ret[next(iter(ret))]['changes'])
  3038. self.assertEqual(self._read(name), self.with_matching_block)
  3039. # Pass 2a: Re-run state, no changes should be made
  3040. ret = self.run_state('file.blockreplace',
  3041. name=name,
  3042. content=self.content.rstrip('\r\n'),
  3043. marker_start=self.marker_start,
  3044. marker_end=self.marker_end,
  3045. append_newline=True)
  3046. self.assertSaltTrueReturn(ret)
  3047. self.assertFalse(ret[next(iter(ret))]['changes'])
  3048. self.assertEqual(self._read(name), self.with_matching_block)
  3049. @with_tempfile()
  3050. def test_non_matching_block_no_append_newline(self, name):
  3051. '''
  3052. Test blockreplace when block exists but its contents are not a
  3053. match. Test with append_newline explicitly set to False.
  3054. '''
  3055. # Pass 1: content ends in newline
  3056. self._write(name, self.with_non_matching_block)
  3057. ret = self.run_state('file.blockreplace',
  3058. name=name,
  3059. content=self.content,
  3060. marker_start=self.marker_start,
  3061. marker_end=self.marker_end,
  3062. append_newline=False)
  3063. self.assertSaltTrueReturn(ret)
  3064. self.assertTrue(ret[next(iter(ret))]['changes'])
  3065. self.assertEqual(self._read(name), self.with_matching_block)
  3066. # Pass 1a: Re-run state, no changes should be made
  3067. ret = self.run_state('file.blockreplace',
  3068. name=name,
  3069. content=self.content,
  3070. marker_start=self.marker_start,
  3071. marker_end=self.marker_end,
  3072. append_newline=False)
  3073. self.assertSaltTrueReturn(ret)
  3074. self.assertFalse(ret[next(iter(ret))]['changes'])
  3075. self.assertEqual(self._read(name), self.with_matching_block)
  3076. # Pass 2: content does not end in newline
  3077. self._write(name, self.with_non_matching_block)
  3078. ret = self.run_state('file.blockreplace',
  3079. name=name,
  3080. content=self.content.rstrip('\r\n'),
  3081. marker_start=self.marker_start,
  3082. marker_end=self.marker_end,
  3083. append_newline=False)
  3084. self.assertSaltTrueReturn(ret)
  3085. self.assertTrue(ret[next(iter(ret))]['changes'])
  3086. self.assertEqual(
  3087. self._read(name),
  3088. self.with_matching_block_and_marker_end_not_after_newline)
  3089. # Pass 2a: Re-run state, no changes should be made
  3090. ret = self.run_state('file.blockreplace',
  3091. name=name,
  3092. content=self.content.rstrip('\r\n'),
  3093. marker_start=self.marker_start,
  3094. marker_end=self.marker_end,
  3095. append_newline=False)
  3096. self.assertSaltTrueReturn(ret)
  3097. self.assertFalse(ret[next(iter(ret))]['changes'])
  3098. self.assertEqual(
  3099. self._read(name),
  3100. self.with_matching_block_and_marker_end_not_after_newline)
  3101. @with_tempfile()
  3102. def test_non_matching_block_and_marker_not_after_newline(self, name):
  3103. '''
  3104. Test blockreplace when block exists but its contents are not a
  3105. match, and the marker_end is not directly preceded by a newline.
  3106. '''
  3107. # Pass 1: content ends in newline
  3108. self._write(
  3109. name,
  3110. self.with_non_matching_block_and_marker_end_not_after_newline)
  3111. ret = self.run_state('file.blockreplace',
  3112. name=name,
  3113. content=self.content,
  3114. marker_start=self.marker_start,
  3115. marker_end=self.marker_end)
  3116. self.assertSaltTrueReturn(ret)
  3117. self.assertTrue(ret[next(iter(ret))]['changes'])
  3118. self.assertEqual(self._read(name), self.with_matching_block)
  3119. # Pass 1a: Re-run state, no changes should be made
  3120. ret = self.run_state('file.blockreplace',
  3121. name=name,
  3122. content=self.content,
  3123. marker_start=self.marker_start,
  3124. marker_end=self.marker_end)
  3125. self.assertSaltTrueReturn(ret)
  3126. self.assertFalse(ret[next(iter(ret))]['changes'])
  3127. self.assertEqual(self._read(name), self.with_matching_block)
  3128. # Pass 2: content does not end in newline
  3129. self._write(
  3130. name,
  3131. self.with_non_matching_block_and_marker_end_not_after_newline)
  3132. ret = self.run_state('file.blockreplace',
  3133. name=name,
  3134. content=self.content.rstrip('\r\n'),
  3135. marker_start=self.marker_start,
  3136. marker_end=self.marker_end)
  3137. self.assertSaltTrueReturn(ret)
  3138. self.assertTrue(ret[next(iter(ret))]['changes'])
  3139. self.assertEqual(self._read(name), self.with_matching_block)
  3140. # Pass 2a: Re-run state, no changes should be made
  3141. ret = self.run_state('file.blockreplace',
  3142. name=name,
  3143. content=self.content.rstrip('\r\n'),
  3144. marker_start=self.marker_start,
  3145. marker_end=self.marker_end)
  3146. self.assertSaltTrueReturn(ret)
  3147. self.assertFalse(ret[next(iter(ret))]['changes'])
  3148. self.assertEqual(self._read(name), self.with_matching_block)
  3149. @with_tempfile()
  3150. def test_non_matching_block_and_marker_not_after_newline_append_newline(self, name):
  3151. '''
  3152. Test blockreplace when block exists but its contents are not a match,
  3153. and the marker_end is not directly preceded by a newline. Test with
  3154. append_newline explicitly set to True.
  3155. '''
  3156. # Pass 1: content ends in newline
  3157. self._write(
  3158. name,
  3159. self.with_non_matching_block_and_marker_end_not_after_newline)
  3160. ret = self.run_state('file.blockreplace',
  3161. name=name,
  3162. content=self.content,
  3163. marker_start=self.marker_start,
  3164. marker_end=self.marker_end,
  3165. append_newline=True)
  3166. self.assertSaltTrueReturn(ret)
  3167. self.assertTrue(ret[next(iter(ret))]['changes'])
  3168. self.assertEqual(
  3169. self._read(name),
  3170. self.with_matching_block_and_extra_newline)
  3171. # Pass 1a: Re-run state, no changes should be made
  3172. ret = self.run_state('file.blockreplace',
  3173. name=name,
  3174. content=self.content,
  3175. marker_start=self.marker_start,
  3176. marker_end=self.marker_end,
  3177. append_newline=True)
  3178. self.assertSaltTrueReturn(ret)
  3179. self.assertFalse(ret[next(iter(ret))]['changes'])
  3180. self.assertEqual(
  3181. self._read(name),
  3182. self.with_matching_block_and_extra_newline)
  3183. # Pass 2: content does not end in newline
  3184. self._write(
  3185. name,
  3186. self.with_non_matching_block_and_marker_end_not_after_newline)
  3187. ret = self.run_state('file.blockreplace',
  3188. name=name,
  3189. content=self.content.rstrip('\r\n'),
  3190. marker_start=self.marker_start,
  3191. marker_end=self.marker_end,
  3192. append_newline=True)
  3193. self.assertSaltTrueReturn(ret)
  3194. self.assertTrue(ret[next(iter(ret))]['changes'])
  3195. self.assertEqual(self._read(name), self.with_matching_block)
  3196. # Pass 2a: Re-run state, no changes should be made
  3197. ret = self.run_state('file.blockreplace',
  3198. name=name,
  3199. content=self.content.rstrip('\r\n'),
  3200. marker_start=self.marker_start,
  3201. marker_end=self.marker_end,
  3202. append_newline=True)
  3203. self.assertSaltTrueReturn(ret)
  3204. self.assertFalse(ret[next(iter(ret))]['changes'])
  3205. self.assertEqual(self._read(name), self.with_matching_block)
  3206. @with_tempfile()
  3207. def test_non_matching_block_and_marker_not_after_newline_no_append_newline(self, name):
  3208. '''
  3209. Test blockreplace when block exists but its contents are not a match,
  3210. and the marker_end is not directly preceded by a newline. Test with
  3211. append_newline explicitly set to False.
  3212. '''
  3213. # Pass 1: content ends in newline
  3214. self._write(
  3215. name,
  3216. self.with_non_matching_block_and_marker_end_not_after_newline)
  3217. ret = self.run_state('file.blockreplace',
  3218. name=name,
  3219. content=self.content,
  3220. marker_start=self.marker_start,
  3221. marker_end=self.marker_end,
  3222. append_newline=False)
  3223. self.assertSaltTrueReturn(ret)
  3224. self.assertTrue(ret[next(iter(ret))]['changes'])
  3225. self.assertEqual(self._read(name), self.with_matching_block)
  3226. # Pass 1a: Re-run state, no changes should be made
  3227. ret = self.run_state('file.blockreplace',
  3228. name=name,
  3229. content=self.content,
  3230. marker_start=self.marker_start,
  3231. marker_end=self.marker_end,
  3232. append_newline=False)
  3233. self.assertSaltTrueReturn(ret)
  3234. self.assertFalse(ret[next(iter(ret))]['changes'])
  3235. self.assertEqual(self._read(name), self.with_matching_block)
  3236. # Pass 2: content does not end in newline
  3237. self._write(
  3238. name,
  3239. self.with_non_matching_block_and_marker_end_not_after_newline)
  3240. ret = self.run_state('file.blockreplace',
  3241. name=name,
  3242. content=self.content.rstrip('\r\n'),
  3243. marker_start=self.marker_start,
  3244. marker_end=self.marker_end,
  3245. append_newline=False)
  3246. self.assertSaltTrueReturn(ret)
  3247. self.assertTrue(ret[next(iter(ret))]['changes'])
  3248. self.assertEqual(
  3249. self._read(name),
  3250. self.with_matching_block_and_marker_end_not_after_newline)
  3251. # Pass 2a: Re-run state, no changes should be made
  3252. ret = self.run_state('file.blockreplace',
  3253. name=name,
  3254. content=self.content.rstrip('\r\n'),
  3255. marker_start=self.marker_start,
  3256. marker_end=self.marker_end,
  3257. append_newline=False)
  3258. self.assertSaltTrueReturn(ret)
  3259. self.assertFalse(ret[next(iter(ret))]['changes'])
  3260. self.assertEqual(
  3261. self._read(name),
  3262. self.with_matching_block_and_marker_end_not_after_newline)
  3263. @with_tempfile()
  3264. def test_matching_block(self, name):
  3265. '''
  3266. Test blockreplace when block exists and its contents are a match. No
  3267. changes should be made.
  3268. '''
  3269. # Pass 1: content ends in newline
  3270. self._write(name, self.with_matching_block)
  3271. ret = self.run_state('file.blockreplace',
  3272. name=name,
  3273. content=self.content,
  3274. marker_start=self.marker_start,
  3275. marker_end=self.marker_end)
  3276. self.assertSaltTrueReturn(ret)
  3277. self.assertFalse(ret[next(iter(ret))]['changes'])
  3278. self.assertEqual(self._read(name), self.with_matching_block)
  3279. # Pass 1a: Re-run state, no changes should be made
  3280. ret = self.run_state('file.blockreplace',
  3281. name=name,
  3282. content=self.content,
  3283. marker_start=self.marker_start,
  3284. marker_end=self.marker_end)
  3285. self.assertSaltTrueReturn(ret)
  3286. self.assertFalse(ret[next(iter(ret))]['changes'])
  3287. self.assertEqual(self._read(name), self.with_matching_block)
  3288. # Pass 2: content does not end in newline
  3289. self._write(name, self.with_matching_block)
  3290. ret = self.run_state('file.blockreplace',
  3291. name=name,
  3292. content=self.content.rstrip('\r\n'),
  3293. marker_start=self.marker_start,
  3294. marker_end=self.marker_end)
  3295. self.assertSaltTrueReturn(ret)
  3296. self.assertFalse(ret[next(iter(ret))]['changes'])
  3297. self.assertEqual(self._read(name), self.with_matching_block)
  3298. # Pass 2a: Re-run state, no changes should be made
  3299. ret = self.run_state('file.blockreplace',
  3300. name=name,
  3301. content=self.content.rstrip('\r\n'),
  3302. marker_start=self.marker_start,
  3303. marker_end=self.marker_end)
  3304. self.assertSaltTrueReturn(ret)
  3305. self.assertFalse(ret[next(iter(ret))]['changes'])
  3306. self.assertEqual(self._read(name), self.with_matching_block)
  3307. @with_tempfile()
  3308. def test_matching_block_append_newline(self, name):
  3309. '''
  3310. Test blockreplace when block exists and its contents are a match. Test
  3311. with append_newline explicitly set to True. This will result in an
  3312. extra newline when the content ends in a newline, and will not when the
  3313. content does not end in a newline.
  3314. '''
  3315. # Pass 1: content ends in newline
  3316. self._write(name, self.with_matching_block)
  3317. ret = self.run_state('file.blockreplace',
  3318. name=name,
  3319. content=self.content,
  3320. marker_start=self.marker_start,
  3321. marker_end=self.marker_end,
  3322. append_newline=True)
  3323. self.assertSaltTrueReturn(ret)
  3324. self.assertTrue(ret[next(iter(ret))]['changes'])
  3325. self.assertEqual(
  3326. self._read(name),
  3327. self.with_matching_block_and_extra_newline)
  3328. # Pass 1a: Re-run state, no changes should be made
  3329. ret = self.run_state('file.blockreplace',
  3330. name=name,
  3331. content=self.content,
  3332. marker_start=self.marker_start,
  3333. marker_end=self.marker_end,
  3334. append_newline=True)
  3335. self.assertSaltTrueReturn(ret)
  3336. self.assertFalse(ret[next(iter(ret))]['changes'])
  3337. self.assertEqual(
  3338. self._read(name),
  3339. self.with_matching_block_and_extra_newline)
  3340. # Pass 2: content does not end in newline
  3341. self._write(name, self.with_matching_block)
  3342. ret = self.run_state('file.blockreplace',
  3343. name=name,
  3344. content=self.content.rstrip('\r\n'),
  3345. marker_start=self.marker_start,
  3346. marker_end=self.marker_end,
  3347. append_newline=True)
  3348. self.assertSaltTrueReturn(ret)
  3349. self.assertFalse(ret[next(iter(ret))]['changes'])
  3350. self.assertEqual(self._read(name), self.with_matching_block)
  3351. # Pass 2a: Re-run state, no changes should be made
  3352. ret = self.run_state('file.blockreplace',
  3353. name=name,
  3354. content=self.content.rstrip('\r\n'),
  3355. marker_start=self.marker_start,
  3356. marker_end=self.marker_end,
  3357. append_newline=True)
  3358. self.assertSaltTrueReturn(ret)
  3359. self.assertFalse(ret[next(iter(ret))]['changes'])
  3360. self.assertEqual(self._read(name), self.with_matching_block)
  3361. @with_tempfile()
  3362. def test_matching_block_no_append_newline(self, name):
  3363. '''
  3364. Test blockreplace when block exists and its contents are a match. Test
  3365. with append_newline explicitly set to False. This will result in the
  3366. marker_end not being directly preceded by a newline when the content
  3367. does not end in a newline.
  3368. '''
  3369. # Pass 1: content ends in newline
  3370. self._write(name, self.with_matching_block)
  3371. ret = self.run_state('file.blockreplace',
  3372. name=name,
  3373. content=self.content,
  3374. marker_start=self.marker_start,
  3375. marker_end=self.marker_end,
  3376. append_newline=False)
  3377. self.assertSaltTrueReturn(ret)
  3378. self.assertFalse(ret[next(iter(ret))]['changes'])
  3379. self.assertEqual(self._read(name), self.with_matching_block)
  3380. # Pass 1a: Re-run state, no changes should be made
  3381. ret = self.run_state('file.blockreplace',
  3382. name=name,
  3383. content=self.content,
  3384. marker_start=self.marker_start,
  3385. marker_end=self.marker_end,
  3386. append_newline=False)
  3387. self.assertSaltTrueReturn(ret)
  3388. self.assertFalse(ret[next(iter(ret))]['changes'])
  3389. self.assertEqual(self._read(name), self.with_matching_block)
  3390. # Pass 2: content does not end in newline
  3391. self._write(name, self.with_matching_block)
  3392. ret = self.run_state('file.blockreplace',
  3393. name=name,
  3394. content=self.content.rstrip('\r\n'),
  3395. marker_start=self.marker_start,
  3396. marker_end=self.marker_end,
  3397. append_newline=False)
  3398. self.assertSaltTrueReturn(ret)
  3399. self.assertTrue(ret[next(iter(ret))]['changes'])
  3400. self.assertEqual(
  3401. self._read(name),
  3402. self.with_matching_block_and_marker_end_not_after_newline)
  3403. # Pass 2a: Re-run state, no changes should be made
  3404. ret = self.run_state('file.blockreplace',
  3405. name=name,
  3406. content=self.content.rstrip('\r\n'),
  3407. marker_start=self.marker_start,
  3408. marker_end=self.marker_end,
  3409. append_newline=False)
  3410. self.assertSaltTrueReturn(ret)
  3411. self.assertFalse(ret[next(iter(ret))]['changes'])
  3412. self.assertEqual(
  3413. self._read(name),
  3414. self.with_matching_block_and_marker_end_not_after_newline)
  3415. @with_tempfile()
  3416. def test_matching_block_and_marker_not_after_newline(self, name):
  3417. '''
  3418. Test blockreplace when block exists and its contents are a match, but
  3419. the marker_end is not directly preceded by a newline.
  3420. '''
  3421. # Pass 1: content ends in newline
  3422. self._write(
  3423. name,
  3424. self.with_matching_block_and_marker_end_not_after_newline)
  3425. ret = self.run_state('file.blockreplace',
  3426. name=name,
  3427. content=self.content,
  3428. marker_start=self.marker_start,
  3429. marker_end=self.marker_end)
  3430. self.assertSaltTrueReturn(ret)
  3431. self.assertTrue(ret[next(iter(ret))]['changes'])
  3432. self.assertEqual(self._read(name), self.with_matching_block)
  3433. # Pass 1a: Re-run state, no changes should be made
  3434. ret = self.run_state('file.blockreplace',
  3435. name=name,
  3436. content=self.content,
  3437. marker_start=self.marker_start,
  3438. marker_end=self.marker_end)
  3439. self.assertSaltTrueReturn(ret)
  3440. self.assertFalse(ret[next(iter(ret))]['changes'])
  3441. self.assertEqual(self._read(name), self.with_matching_block)
  3442. # Pass 2: content does not end in newline
  3443. self._write(
  3444. name,
  3445. self.with_matching_block_and_marker_end_not_after_newline)
  3446. ret = self.run_state('file.blockreplace',
  3447. name=name,
  3448. content=self.content.rstrip('\r\n'),
  3449. marker_start=self.marker_start,
  3450. marker_end=self.marker_end)
  3451. self.assertSaltTrueReturn(ret)
  3452. self.assertTrue(ret[next(iter(ret))]['changes'])
  3453. self.assertEqual(self._read(name), self.with_matching_block)
  3454. # Pass 2a: Re-run state, no changes should be made
  3455. ret = self.run_state('file.blockreplace',
  3456. name=name,
  3457. content=self.content.rstrip('\r\n'),
  3458. marker_start=self.marker_start,
  3459. marker_end=self.marker_end)
  3460. self.assertSaltTrueReturn(ret)
  3461. self.assertFalse(ret[next(iter(ret))]['changes'])
  3462. self.assertEqual(self._read(name), self.with_matching_block)
  3463. @with_tempfile()
  3464. def test_matching_block_and_marker_not_after_newline_append_newline(self, name):
  3465. '''
  3466. Test blockreplace when block exists and its contents are a match, but
  3467. the marker_end is not directly preceded by a newline. Test with
  3468. append_newline explicitly set to True. This will result in an extra
  3469. newline when the content ends in a newline, and will not when the
  3470. content does not end in a newline.
  3471. '''
  3472. # Pass 1: content ends in newline
  3473. self._write(
  3474. name,
  3475. self.with_matching_block_and_marker_end_not_after_newline)
  3476. ret = self.run_state('file.blockreplace',
  3477. name=name,
  3478. content=self.content,
  3479. marker_start=self.marker_start,
  3480. marker_end=self.marker_end,
  3481. append_newline=True)
  3482. self.assertSaltTrueReturn(ret)
  3483. self.assertTrue(ret[next(iter(ret))]['changes'])
  3484. self.assertEqual(
  3485. self._read(name),
  3486. self.with_matching_block_and_extra_newline)
  3487. # Pass 1a: Re-run state, no changes should be made
  3488. ret = self.run_state('file.blockreplace',
  3489. name=name,
  3490. content=self.content,
  3491. marker_start=self.marker_start,
  3492. marker_end=self.marker_end,
  3493. append_newline=True)
  3494. self.assertSaltTrueReturn(ret)
  3495. self.assertFalse(ret[next(iter(ret))]['changes'])
  3496. self.assertEqual(
  3497. self._read(name),
  3498. self.with_matching_block_and_extra_newline)
  3499. # Pass 2: content does not end in newline
  3500. self._write(
  3501. name,
  3502. self.with_matching_block_and_marker_end_not_after_newline)
  3503. ret = self.run_state('file.blockreplace',
  3504. name=name,
  3505. content=self.content.rstrip('\r\n'),
  3506. marker_start=self.marker_start,
  3507. marker_end=self.marker_end,
  3508. append_newline=True)
  3509. self.assertSaltTrueReturn(ret)
  3510. self.assertTrue(ret[next(iter(ret))]['changes'])
  3511. self.assertEqual(self._read(name), self.with_matching_block)
  3512. # Pass 2a: Re-run state, no changes should be made
  3513. ret = self.run_state('file.blockreplace',
  3514. name=name,
  3515. content=self.content.rstrip('\r\n'),
  3516. marker_start=self.marker_start,
  3517. marker_end=self.marker_end,
  3518. append_newline=True)
  3519. self.assertSaltTrueReturn(ret)
  3520. self.assertFalse(ret[next(iter(ret))]['changes'])
  3521. self.assertEqual(self._read(name), self.with_matching_block)
  3522. @with_tempfile()
  3523. def test_matching_block_and_marker_not_after_newline_no_append_newline(self, name):
  3524. '''
  3525. Test blockreplace when block exists and its contents are a match, but
  3526. the marker_end is not directly preceded by a newline. Test with
  3527. append_newline explicitly set to False.
  3528. '''
  3529. # Pass 1: content ends in newline
  3530. self._write(
  3531. name,
  3532. self.with_matching_block_and_marker_end_not_after_newline)
  3533. ret = self.run_state('file.blockreplace',
  3534. name=name,
  3535. content=self.content,
  3536. marker_start=self.marker_start,
  3537. marker_end=self.marker_end,
  3538. append_newline=False)
  3539. self.assertSaltTrueReturn(ret)
  3540. self.assertTrue(ret[next(iter(ret))]['changes'])
  3541. self.assertEqual(self._read(name), self.with_matching_block)
  3542. # Pass 1a: Re-run state, no changes should be made
  3543. ret = self.run_state('file.blockreplace',
  3544. name=name,
  3545. content=self.content,
  3546. marker_start=self.marker_start,
  3547. marker_end=self.marker_end,
  3548. append_newline=False)
  3549. self.assertSaltTrueReturn(ret)
  3550. self.assertFalse(ret[next(iter(ret))]['changes'])
  3551. self.assertEqual(self._read(name), self.with_matching_block)
  3552. # Pass 2: content does not end in newline
  3553. self._write(
  3554. name,
  3555. self.with_matching_block_and_marker_end_not_after_newline)
  3556. ret = self.run_state('file.blockreplace',
  3557. name=name,
  3558. content=self.content.rstrip('\r\n'),
  3559. marker_start=self.marker_start,
  3560. marker_end=self.marker_end,
  3561. append_newline=False)
  3562. self.assertSaltTrueReturn(ret)
  3563. self.assertFalse(ret[next(iter(ret))]['changes'])
  3564. self.assertEqual(
  3565. self._read(name),
  3566. self.with_matching_block_and_marker_end_not_after_newline)
  3567. # Pass 2a: Re-run state, no changes should be made
  3568. ret = self.run_state('file.blockreplace',
  3569. name=name,
  3570. content=self.content.rstrip('\r\n'),
  3571. marker_start=self.marker_start,
  3572. marker_end=self.marker_end,
  3573. append_newline=False)
  3574. self.assertSaltTrueReturn(ret)
  3575. self.assertFalse(ret[next(iter(ret))]['changes'])
  3576. self.assertEqual(
  3577. self._read(name),
  3578. self.with_matching_block_and_marker_end_not_after_newline)
  3579. @with_tempfile()
  3580. def test_issue_49043(self, name):
  3581. ret = self.run_function(
  3582. 'state.sls',
  3583. mods='issue-49043',
  3584. pillar={'name': name},
  3585. )
  3586. log.error("ret = %s", repr(ret))
  3587. diff = '--- \n+++ \n@@ -0,0 +1,3 @@\n'
  3588. diff += dedent('''\
  3589. +#-- start managed zone --
  3590. +äöü
  3591. +#-- end managed zone --
  3592. ''')
  3593. job = 'file_|-somefile-blockreplace_|-{}_|-blockreplace'.format(name)
  3594. self.assertEqual(
  3595. ret[job]['changes']['diff'],
  3596. diff)
  3597. class RemoteFileTest(ModuleCase, SaltReturnAssertsMixin):
  3598. '''
  3599. Uses a local tornado webserver to test http(s) file.managed states with and
  3600. without skip_verify
  3601. '''
  3602. @classmethod
  3603. def setUpClass(cls):
  3604. cls.webserver = Webserver()
  3605. cls.webserver.start()
  3606. cls.source = cls.webserver.url('grail/scene33')
  3607. if IS_WINDOWS:
  3608. # CRLF vs LF causes a different hash on windows
  3609. cls.source_hash = '21438b3d5fd2c0028bcab92f7824dc69'
  3610. else:
  3611. cls.source_hash = 'd2feb3beb323c79fc7a0f44f1408b4a3'
  3612. @classmethod
  3613. def tearDownClass(cls):
  3614. cls.webserver.stop()
  3615. @with_tempfile(create=False)
  3616. def setUp(self, name): # pylint: disable=arguments-differ
  3617. self.name = name
  3618. def tearDown(self):
  3619. try:
  3620. os.remove(self.name)
  3621. except OSError as exc:
  3622. if exc.errno != errno.ENOENT:
  3623. raise exc
  3624. def run_state(self, *args, **kwargs):
  3625. ret = super(RemoteFileTest, self).run_state(*args, **kwargs)
  3626. log.debug('ret = %s', ret)
  3627. return ret
  3628. def test_file_managed_http_source_no_hash(self):
  3629. '''
  3630. Test a remote file with no hash
  3631. '''
  3632. ret = self.run_state('file.managed',
  3633. name=self.name,
  3634. source=self.source,
  3635. skip_verify=False)
  3636. # This should fail because no hash was provided
  3637. self.assertSaltFalseReturn(ret)
  3638. def test_file_managed_http_source(self):
  3639. '''
  3640. Test a remote file with no hash
  3641. '''
  3642. ret = self.run_state('file.managed',
  3643. name=self.name,
  3644. source=self.source,
  3645. source_hash=self.source_hash,
  3646. skip_verify=False)
  3647. self.assertSaltTrueReturn(ret)
  3648. def test_file_managed_http_source_skip_verify(self):
  3649. '''
  3650. Test a remote file using skip_verify
  3651. '''
  3652. ret = self.run_state('file.managed',
  3653. name=self.name,
  3654. source=self.source,
  3655. skip_verify=True)
  3656. self.assertSaltTrueReturn(ret)
  3657. def test_file_managed_keep_source_false_http(self):
  3658. '''
  3659. This test ensures that we properly clean the cached file if keep_source
  3660. is set to False, for source files using an http:// URL
  3661. '''
  3662. # Run the state
  3663. ret = self.run_state('file.managed',
  3664. name=self.name,
  3665. source=self.source,
  3666. source_hash=self.source_hash,
  3667. keep_source=False)
  3668. ret = ret[next(iter(ret))]
  3669. assert ret['result'] is True
  3670. # Now make sure that the file is not cached
  3671. result = self.run_function('cp.is_cached', [self.source])
  3672. assert result == '', 'File is still cached at {0}'.format(result)
  3673. @skipIf(not salt.utils.path.which('patch'), 'patch is not installed')
  3674. class PatchTest(ModuleCase, SaltReturnAssertsMixin):
  3675. def _check_patch_version(self, min_version):
  3676. '''
  3677. patch version check
  3678. '''
  3679. version = self.run_function('cmd.run', ['patch --version']).splitlines()[0]
  3680. version = version.split()[1]
  3681. if _LooseVersion(version) < _LooseVersion(min_version):
  3682. self.skipTest('Minimum patch version required: {0}. '
  3683. 'Patch version installed: {1}'.format(min_version, version))
  3684. @classmethod
  3685. def setUpClass(cls):
  3686. cls.webserver = Webserver()
  3687. cls.webserver.start()
  3688. cls.numbers_patch_name = 'numbers.patch'
  3689. cls.math_patch_name = 'math.patch'
  3690. cls.all_patch_name = 'all.patch'
  3691. cls.numbers_patch_template_name = cls.numbers_patch_name + '.jinja'
  3692. cls.math_patch_template_name = cls.math_patch_name + '.jinja'
  3693. cls.all_patch_template_name = cls.all_patch_name + '.jinja'
  3694. cls.numbers_patch_path = 'patches/' + cls.numbers_patch_name
  3695. cls.math_patch_path = 'patches/' + cls.math_patch_name
  3696. cls.all_patch_path = 'patches/' + cls.all_patch_name
  3697. cls.numbers_patch_template_path = \
  3698. 'patches/' + cls.numbers_patch_template_name
  3699. cls.math_patch_template_path = \
  3700. 'patches/' + cls.math_patch_template_name
  3701. cls.all_patch_template_path = \
  3702. 'patches/' + cls.all_patch_template_name
  3703. cls.numbers_patch = 'salt://' + cls.numbers_patch_path
  3704. cls.math_patch = 'salt://' + cls.math_patch_path
  3705. cls.all_patch = 'salt://' + cls.all_patch_path
  3706. cls.numbers_patch_template = 'salt://' + cls.numbers_patch_template_path
  3707. cls.math_patch_template = 'salt://' + cls.math_patch_template_path
  3708. cls.all_patch_template = 'salt://' + cls.all_patch_template_path
  3709. cls.numbers_patch_http = cls.webserver.url(cls.numbers_patch_path)
  3710. cls.math_patch_http = cls.webserver.url(cls.math_patch_path)
  3711. cls.all_patch_http = cls.webserver.url(cls.all_patch_path)
  3712. cls.numbers_patch_template_http = \
  3713. cls.webserver.url(cls.numbers_patch_template_path)
  3714. cls.math_patch_template_http = \
  3715. cls.webserver.url(cls.math_patch_template_path)
  3716. cls.all_patch_template_http = \
  3717. cls.webserver.url(cls.all_patch_template_path)
  3718. patches_dir = os.path.join(FILES, 'file', 'base', 'patches')
  3719. cls.numbers_patch_hash = salt.utils.hashutils.get_hash(
  3720. os.path.join(patches_dir, cls.numbers_patch_name)
  3721. )
  3722. cls.math_patch_hash = salt.utils.hashutils.get_hash(
  3723. os.path.join(patches_dir, cls.math_patch_name)
  3724. )
  3725. cls.all_patch_hash = salt.utils.hashutils.get_hash(
  3726. os.path.join(patches_dir, cls.all_patch_name)
  3727. )
  3728. cls.numbers_patch_template_hash = salt.utils.hashutils.get_hash(
  3729. os.path.join(patches_dir, cls.numbers_patch_template_name)
  3730. )
  3731. cls.math_patch_template_hash = salt.utils.hashutils.get_hash(
  3732. os.path.join(patches_dir, cls.math_patch_template_name)
  3733. )
  3734. cls.all_patch_template_hash = salt.utils.hashutils.get_hash(
  3735. os.path.join(patches_dir, cls.all_patch_template_name)
  3736. )
  3737. cls.context = {'two': 'two', 'ten': 10}
  3738. @classmethod
  3739. def tearDownClass(cls):
  3740. cls.webserver.stop()
  3741. def setUp(self):
  3742. '''
  3743. Create a new unpatched set of files
  3744. '''
  3745. self.base_dir = tempfile.mkdtemp(dir=TMP)
  3746. os.makedirs(os.path.join(self.base_dir, 'foo', 'bar'))
  3747. self.numbers_file = os.path.join(self.base_dir, 'foo', 'numbers.txt')
  3748. self.math_file = os.path.join(self.base_dir, 'foo', 'bar', 'math.txt')
  3749. with salt.utils.files.fopen(self.numbers_file, 'w') as fp_:
  3750. fp_.write(textwrap.dedent('''\
  3751. one
  3752. two
  3753. three
  3754. 1
  3755. 2
  3756. 3
  3757. '''))
  3758. with salt.utils.files.fopen(self.math_file, 'w') as fp_:
  3759. fp_.write(textwrap.dedent('''\
  3760. Five plus five is ten
  3761. Four squared is sixteen
  3762. '''))
  3763. self.addCleanup(shutil.rmtree, self.base_dir, ignore_errors=True)
  3764. def test_patch_single_file(self):
  3765. '''
  3766. Test file.patch using a patch applied to a single file
  3767. '''
  3768. ret = self.run_state(
  3769. 'file.patch',
  3770. name=self.numbers_file,
  3771. source=self.numbers_patch,
  3772. )
  3773. self.assertSaltTrueReturn(ret)
  3774. ret = ret[next(iter(ret))]
  3775. self.assertEqual(ret['comment'], 'Patch successfully applied')
  3776. # Re-run the state, should succeed and there should be a message about
  3777. # a partially-applied hunk.
  3778. ret = self.run_state(
  3779. 'file.patch',
  3780. name=self.numbers_file,
  3781. source=self.numbers_patch,
  3782. )
  3783. self.assertSaltTrueReturn(ret)
  3784. ret = ret[next(iter(ret))]
  3785. self.assertEqual(ret['comment'], 'Patch was already applied')
  3786. self.assertEqual(ret['changes'], {})
  3787. def test_patch_directory(self):
  3788. '''
  3789. Test file.patch using a patch applied to a directory, with changes
  3790. spanning multiple files.
  3791. '''
  3792. self._check_patch_version('2.6')
  3793. ret = self.run_state(
  3794. 'file.patch',
  3795. name=self.base_dir,
  3796. source=self.all_patch,
  3797. strip=1,
  3798. )
  3799. self.assertSaltTrueReturn(ret)
  3800. ret = ret[next(iter(ret))]
  3801. self.assertEqual(ret['comment'], 'Patch successfully applied')
  3802. # Re-run the state, should succeed and there should be a message about
  3803. # a partially-applied hunk.
  3804. ret = self.run_state(
  3805. 'file.patch',
  3806. name=self.base_dir,
  3807. source=self.all_patch,
  3808. strip=1,
  3809. )
  3810. self.assertSaltTrueReturn(ret)
  3811. ret = ret[next(iter(ret))]
  3812. self.assertEqual(ret['comment'], 'Patch was already applied')
  3813. self.assertEqual(ret['changes'], {})
  3814. def test_patch_strip_parsing(self):
  3815. '''
  3816. Test that we successfuly parse -p/--strip when included in the options
  3817. '''
  3818. self._check_patch_version('2.6')
  3819. # Run the state using -p1
  3820. ret = self.run_state(
  3821. 'file.patch',
  3822. name=self.base_dir,
  3823. source=self.all_patch,
  3824. options='-p1',
  3825. )
  3826. self.assertSaltTrueReturn(ret)
  3827. ret = ret[next(iter(ret))]
  3828. self.assertEqual(ret['comment'], 'Patch successfully applied')
  3829. # Re-run the state using --strip=1
  3830. ret = self.run_state(
  3831. 'file.patch',
  3832. name=self.base_dir,
  3833. source=self.all_patch,
  3834. options='--strip=1',
  3835. )
  3836. self.assertSaltTrueReturn(ret)
  3837. ret = ret[next(iter(ret))]
  3838. self.assertEqual(ret['comment'], 'Patch was already applied')
  3839. self.assertEqual(ret['changes'], {})
  3840. # Re-run the state using --strip 1
  3841. ret = self.run_state(
  3842. 'file.patch',
  3843. name=self.base_dir,
  3844. source=self.all_patch,
  3845. options='--strip 1',
  3846. )
  3847. self.assertSaltTrueReturn(ret)
  3848. ret = ret[next(iter(ret))]
  3849. self.assertEqual(ret['comment'], 'Patch was already applied')
  3850. self.assertEqual(ret['changes'], {})
  3851. def test_patch_saltenv(self):
  3852. '''
  3853. Test that we attempt to download the patch from a non-base saltenv
  3854. '''
  3855. # This state will fail because we don't have a patch file in that
  3856. # environment, but that is OK, we just want to test that we're looking
  3857. # in an environment other than base.
  3858. ret = self.run_state(
  3859. 'file.patch',
  3860. name=self.math_file,
  3861. source=self.math_patch,
  3862. saltenv='prod',
  3863. )
  3864. self.assertSaltFalseReturn(ret)
  3865. ret = ret[next(iter(ret))]
  3866. self.assertEqual(
  3867. ret['comment'],
  3868. "Source file {0} not found in saltenv 'prod'".format(self.math_patch)
  3869. )
  3870. def test_patch_single_file_failure(self):
  3871. '''
  3872. Test file.patch using a patch applied to a single file. This tests a
  3873. failed patch.
  3874. '''
  3875. # Empty the file to ensure that the patch doesn't apply cleanly
  3876. with salt.utils.files.fopen(self.numbers_file, 'w'):
  3877. pass
  3878. ret = self.run_state(
  3879. 'file.patch',
  3880. name=self.numbers_file,
  3881. source=self.numbers_patch,
  3882. )
  3883. self.assertSaltFalseReturn(ret)
  3884. ret = ret[next(iter(ret))]
  3885. self.assertIn('Patch would not apply cleanly', ret['comment'])
  3886. # Test the reject_file option and ensure that the rejects are written
  3887. # to the path specified.
  3888. reject_file = salt.utils.files.mkstemp()
  3889. ret = self.run_state(
  3890. 'file.patch',
  3891. name=self.numbers_file,
  3892. source=self.numbers_patch,
  3893. reject_file=reject_file,
  3894. strip=1,
  3895. )
  3896. self.assertSaltFalseReturn(ret)
  3897. ret = ret[next(iter(ret))]
  3898. self.assertIn('Patch would not apply cleanly', ret['comment'])
  3899. self.assertIn(
  3900. 'saving rejects to file {0}'.format(reject_file),
  3901. ret['comment']
  3902. )
  3903. def test_patch_directory_failure(self):
  3904. '''
  3905. Test file.patch using a patch applied to a directory, with changes
  3906. spanning multiple files.
  3907. '''
  3908. # Empty the file to ensure that the patch doesn't apply
  3909. with salt.utils.files.fopen(self.math_file, 'w'):
  3910. pass
  3911. ret = self.run_state(
  3912. 'file.patch',
  3913. name=self.base_dir,
  3914. source=self.all_patch,
  3915. strip=1,
  3916. )
  3917. self.assertSaltFalseReturn(ret)
  3918. ret = ret[next(iter(ret))]
  3919. self.assertIn('Patch would not apply cleanly', ret['comment'])
  3920. # Test the reject_file option and ensure that the rejects are written
  3921. # to the path specified.
  3922. reject_file = salt.utils.files.mkstemp()
  3923. ret = self.run_state(
  3924. 'file.patch',
  3925. name=self.base_dir,
  3926. source=self.all_patch,
  3927. reject_file=reject_file,
  3928. strip=1,
  3929. )
  3930. self.assertSaltFalseReturn(ret)
  3931. ret = ret[next(iter(ret))]
  3932. self.assertIn('Patch would not apply cleanly', ret['comment'])
  3933. self.assertIn(
  3934. 'saving rejects to file {0}'.format(reject_file),
  3935. ret['comment']
  3936. )
  3937. def test_patch_single_file_remote_source(self):
  3938. '''
  3939. Test file.patch using a patch applied to a single file, with the patch
  3940. coming from a remote source.
  3941. '''
  3942. # Try without a source_hash and without skip_verify=True, this should
  3943. # fail with a message about the source_hash
  3944. ret = self.run_state(
  3945. 'file.patch',
  3946. name=self.math_file,
  3947. source=self.math_patch_http,
  3948. )
  3949. self.assertSaltFalseReturn(ret)
  3950. ret = ret[next(iter(ret))]
  3951. self.assertIn('Unable to verify upstream hash', ret['comment'])
  3952. # Re-run the state with a source hash, it should now succeed
  3953. ret = self.run_state(
  3954. 'file.patch',
  3955. name=self.math_file,
  3956. source=self.math_patch_http,
  3957. source_hash=self.math_patch_hash,
  3958. )
  3959. self.assertSaltTrueReturn(ret)
  3960. ret = ret[next(iter(ret))]
  3961. self.assertEqual(ret['comment'], 'Patch successfully applied')
  3962. # Re-run again, this time with no hash and skip_verify=True to test
  3963. # skipping hash verification
  3964. ret = self.run_state(
  3965. 'file.patch',
  3966. name=self.math_file,
  3967. source=self.math_patch_http,
  3968. skip_verify=True,
  3969. )
  3970. self.assertSaltTrueReturn(ret)
  3971. ret = ret[next(iter(ret))]
  3972. self.assertEqual(ret['comment'], 'Patch was already applied')
  3973. self.assertEqual(ret['changes'], {})
  3974. def test_patch_directory_remote_source(self):
  3975. '''
  3976. Test file.patch using a patch applied to a directory, with changes
  3977. spanning multiple files, and the patch file coming from a remote
  3978. source.
  3979. '''
  3980. self._check_patch_version('2.6')
  3981. # Try without a source_hash and without skip_verify=True, this should
  3982. # fail with a message about the source_hash
  3983. ret = self.run_state(
  3984. 'file.patch',
  3985. name=self.base_dir,
  3986. source=self.all_patch_http,
  3987. strip=1,
  3988. )
  3989. self.assertSaltFalseReturn(ret)
  3990. ret = ret[next(iter(ret))]
  3991. self.assertIn('Unable to verify upstream hash', ret['comment'])
  3992. # Re-run the state with a source hash, it should now succeed
  3993. ret = self.run_state(
  3994. 'file.patch',
  3995. name=self.base_dir,
  3996. source=self.all_patch_http,
  3997. source_hash=self.all_patch_hash,
  3998. strip=1,
  3999. )
  4000. self.assertSaltTrueReturn(ret)
  4001. ret = ret[next(iter(ret))]
  4002. self.assertEqual(ret['comment'], 'Patch successfully applied')
  4003. # Re-run again, this time with no hash and skip_verify=True to test
  4004. # skipping hash verification
  4005. ret = self.run_state(
  4006. 'file.patch',
  4007. name=self.base_dir,
  4008. source=self.all_patch_http,
  4009. strip=1,
  4010. skip_verify=True,
  4011. )
  4012. self.assertSaltTrueReturn(ret)
  4013. ret = ret[next(iter(ret))]
  4014. self.assertEqual(ret['comment'], 'Patch was already applied')
  4015. self.assertEqual(ret['changes'], {})
  4016. def test_patch_single_file_template(self):
  4017. '''
  4018. Test file.patch using a patch applied to a single file, with jinja
  4019. templating applied to the patch file.
  4020. '''
  4021. ret = self.run_state(
  4022. 'file.patch',
  4023. name=self.numbers_file,
  4024. source=self.numbers_patch_template,
  4025. template='jinja',
  4026. context=self.context,
  4027. )
  4028. self.assertSaltTrueReturn(ret)
  4029. ret = ret[next(iter(ret))]
  4030. self.assertEqual(ret['comment'], 'Patch successfully applied')
  4031. # Re-run the state, should succeed and there should be a message about
  4032. # a partially-applied hunk.
  4033. ret = self.run_state(
  4034. 'file.patch',
  4035. name=self.numbers_file,
  4036. source=self.numbers_patch_template,
  4037. template='jinja',
  4038. context=self.context,
  4039. )
  4040. self.assertSaltTrueReturn(ret)
  4041. ret = ret[next(iter(ret))]
  4042. self.assertEqual(ret['comment'], 'Patch was already applied')
  4043. self.assertEqual(ret['changes'], {})
  4044. def test_patch_directory_template(self):
  4045. '''
  4046. Test file.patch using a patch applied to a directory, with changes
  4047. spanning multiple files, and with jinja templating applied to the patch
  4048. file.
  4049. '''
  4050. self._check_patch_version('2.6')
  4051. ret = self.run_state(
  4052. 'file.patch',
  4053. name=self.base_dir,
  4054. source=self.all_patch_template,
  4055. template='jinja',
  4056. context=self.context,
  4057. strip=1,
  4058. )
  4059. self.assertSaltTrueReturn(ret)
  4060. ret = ret[next(iter(ret))]
  4061. self.assertEqual(ret['comment'], 'Patch successfully applied')
  4062. # Re-run the state, should succeed and there should be a message about
  4063. # a partially-applied hunk.
  4064. ret = self.run_state(
  4065. 'file.patch',
  4066. name=self.base_dir,
  4067. source=self.all_patch_template,
  4068. template='jinja',
  4069. context=self.context,
  4070. strip=1,
  4071. )
  4072. self.assertSaltTrueReturn(ret)
  4073. ret = ret[next(iter(ret))]
  4074. self.assertEqual(ret['comment'], 'Patch was already applied')
  4075. self.assertEqual(ret['changes'], {})
  4076. def test_patch_single_file_remote_source_template(self):
  4077. '''
  4078. Test file.patch using a patch applied to a single file, with the patch
  4079. coming from a remote source.
  4080. '''
  4081. # Try without a source_hash and without skip_verify=True, this should
  4082. # fail with a message about the source_hash
  4083. ret = self.run_state(
  4084. 'file.patch',
  4085. name=self.math_file,
  4086. source=self.math_patch_template_http,
  4087. template='jinja',
  4088. context=self.context,
  4089. )
  4090. self.assertSaltFalseReturn(ret)
  4091. ret = ret[next(iter(ret))]
  4092. self.assertIn('Unable to verify upstream hash', ret['comment'])
  4093. # Re-run the state with a source hash, it should now succeed
  4094. ret = self.run_state(
  4095. 'file.patch',
  4096. name=self.math_file,
  4097. source=self.math_patch_template_http,
  4098. source_hash=self.math_patch_template_hash,
  4099. template='jinja',
  4100. context=self.context,
  4101. )
  4102. self.assertSaltTrueReturn(ret)
  4103. ret = ret[next(iter(ret))]
  4104. self.assertEqual(ret['comment'], 'Patch successfully applied')
  4105. # Re-run again, this time with no hash and skip_verify=True to test
  4106. # skipping hash verification
  4107. ret = self.run_state(
  4108. 'file.patch',
  4109. name=self.math_file,
  4110. source=self.math_patch_template_http,
  4111. template='jinja',
  4112. context=self.context,
  4113. skip_verify=True,
  4114. )
  4115. self.assertSaltTrueReturn(ret)
  4116. ret = ret[next(iter(ret))]
  4117. self.assertEqual(ret['comment'], 'Patch was already applied')
  4118. self.assertEqual(ret['changes'], {})
  4119. def test_patch_directory_remote_source_template(self):
  4120. '''
  4121. Test file.patch using a patch applied to a directory, with changes
  4122. spanning multiple files, and the patch file coming from a remote
  4123. source.
  4124. '''
  4125. self._check_patch_version('2.6')
  4126. # Try without a source_hash and without skip_verify=True, this should
  4127. # fail with a message about the source_hash
  4128. ret = self.run_state(
  4129. 'file.patch',
  4130. name=self.base_dir,
  4131. source=self.all_patch_template_http,
  4132. template='jinja',
  4133. context=self.context,
  4134. strip=1,
  4135. )
  4136. self.assertSaltFalseReturn(ret)
  4137. ret = ret[next(iter(ret))]
  4138. self.assertIn('Unable to verify upstream hash', ret['comment'])
  4139. # Re-run the state with a source hash, it should now succeed
  4140. ret = self.run_state(
  4141. 'file.patch',
  4142. name=self.base_dir,
  4143. source=self.all_patch_template_http,
  4144. source_hash=self.all_patch_template_hash,
  4145. template='jinja',
  4146. context=self.context,
  4147. strip=1,
  4148. )
  4149. self.assertSaltTrueReturn(ret)
  4150. ret = ret[next(iter(ret))]
  4151. self.assertEqual(ret['comment'], 'Patch successfully applied')
  4152. # Re-run again, this time with no hash and skip_verify=True to test
  4153. # skipping hash verification
  4154. ret = self.run_state(
  4155. 'file.patch',
  4156. name=self.base_dir,
  4157. source=self.all_patch_template_http,
  4158. template='jinja',
  4159. context=self.context,
  4160. strip=1,
  4161. skip_verify=True,
  4162. )
  4163. self.assertSaltTrueReturn(ret)
  4164. ret = ret[next(iter(ret))]
  4165. self.assertEqual(ret['comment'], 'Patch was already applied')
  4166. self.assertEqual(ret['changes'], {})
  4167. def test_patch_test_mode(self):
  4168. '''
  4169. Test file.patch using test=True
  4170. '''
  4171. # Try without a source_hash and without skip_verify=True, this should
  4172. # fail with a message about the source_hash
  4173. ret = self.run_state(
  4174. 'file.patch',
  4175. name=self.numbers_file,
  4176. source=self.numbers_patch,
  4177. test=True,
  4178. )
  4179. self.assertSaltNoneReturn(ret)
  4180. ret = ret[next(iter(ret))]
  4181. self.assertEqual(ret['comment'], 'The patch would be applied')
  4182. self.assertTrue(ret['changes'])
  4183. # Apply the patch for real. We'll then be able to test below that we
  4184. # exit with a True rather than a None result if test=True is used on an
  4185. # already-applied patch.
  4186. ret = self.run_state(
  4187. 'file.patch',
  4188. name=self.numbers_file,
  4189. source=self.numbers_patch,
  4190. )
  4191. self.assertSaltTrueReturn(ret)
  4192. ret = ret[next(iter(ret))]
  4193. self.assertEqual(ret['comment'], 'Patch successfully applied')
  4194. self.assertTrue(ret['changes'])
  4195. # Run again with test=True. Since the pre-check happens before we do
  4196. # the __opts__['test'] check, we should exit with a True result just
  4197. # the same as if we try to run this state on an already-patched file
  4198. # *without* test=True.
  4199. ret = self.run_state(
  4200. 'file.patch',
  4201. name=self.numbers_file,
  4202. source=self.numbers_patch,
  4203. test=True,
  4204. )
  4205. self.assertSaltTrueReturn(ret)
  4206. ret = ret[next(iter(ret))]
  4207. self.assertEqual(ret['comment'], 'Patch was already applied')
  4208. self.assertEqual(ret['changes'], {})
  4209. # Empty the file to ensure that the patch doesn't apply cleanly
  4210. with salt.utils.files.fopen(self.numbers_file, 'w'):
  4211. pass
  4212. # Run again with test=True. Similar to the above run, we are testing
  4213. # that we return before we reach the __opts__['test'] check. In this
  4214. # case we should return a False result because we should already know
  4215. # by this point that the patch will not apply cleanly.
  4216. ret = self.run_state(
  4217. 'file.patch',
  4218. name=self.numbers_file,
  4219. source=self.numbers_patch,
  4220. test=True,
  4221. )
  4222. self.assertSaltFalseReturn(ret)
  4223. ret = ret[next(iter(ret))]
  4224. self.assertIn('Patch would not apply cleanly', ret['comment'])
  4225. self.assertEqual(ret['changes'], {})
  4226. WIN_TEST_FILE = 'c:/testfile'
  4227. @destructiveTest
  4228. @skipIf(not IS_WINDOWS, 'windows test only')
  4229. class WinFileTest(ModuleCase):
  4230. '''
  4231. Test for the file state on Windows
  4232. '''
  4233. def setUp(self):
  4234. self.run_state('file.managed', name=WIN_TEST_FILE, makedirs=True, contents='Only a test')
  4235. def tearDown(self):
  4236. self.run_state('file.absent', name=WIN_TEST_FILE)
  4237. def test_file_managed(self):
  4238. '''
  4239. Test file.managed on Windows
  4240. '''
  4241. self.assertTrue(self.run_state('file.exists', name=WIN_TEST_FILE))
  4242. def test_file_copy(self):
  4243. '''
  4244. Test file.copy on Windows
  4245. '''
  4246. ret = self.run_state('file.copy', name='c:/testfile_copy', makedirs=True, source=WIN_TEST_FILE)
  4247. self.assertTrue(ret)
  4248. def test_file_comment(self):
  4249. '''
  4250. Test file.comment on Windows
  4251. '''
  4252. self.run_state('file.comment', name=WIN_TEST_FILE, regex='^Only')
  4253. with salt.utils.files.fopen(WIN_TEST_FILE, 'r') as fp_:
  4254. self.assertTrue(fp_.read().startswith('#Only'))
  4255. def test_file_replace(self):
  4256. '''
  4257. Test file.replace on Windows
  4258. '''
  4259. self.run_state('file.replace', name=WIN_TEST_FILE, pattern='test', repl='testing')
  4260. with salt.utils.files.fopen(WIN_TEST_FILE, 'r') as fp_:
  4261. self.assertIn('testing', fp_.read())
  4262. def test_file_absent(self):
  4263. '''
  4264. Test file.absent on Windows
  4265. '''
  4266. ret = self.run_state('file.absent', name=WIN_TEST_FILE)
  4267. self.assertTrue(ret)