etch 4.0.0 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Rakefile +7 -8
- data/lib/etch.rb +959 -399
- data/lib/etch/client.rb +265 -335
- metadata +12 -16
data/lib/etch/client.rb
CHANGED
@@ -29,7 +29,6 @@ Silently.silently do
|
|
29
29
|
require 'uri'
|
30
30
|
require 'net/http'
|
31
31
|
require 'net/https'
|
32
|
-
require 'rexml/document'
|
33
32
|
require 'fileutils' # copy, mkpath, rmtree
|
34
33
|
require 'fcntl' # Fcntl::O_*
|
35
34
|
require 'etc' # getpwnam, getgrnam
|
@@ -42,7 +41,7 @@ end
|
|
42
41
|
require 'etch'
|
43
42
|
|
44
43
|
class Etch::Client
|
45
|
-
VERSION = '
|
44
|
+
VERSION = '5.0.0'
|
46
45
|
|
47
46
|
CONFIRM_PROCEED = 1
|
48
47
|
CONFIRM_SKIP = 2
|
@@ -228,12 +227,6 @@ class Etch::Client
|
|
228
227
|
puts "Connecting to #{@filesuri}" if (@debug)
|
229
228
|
http = Net::HTTP.new(@filesuri.host, @filesuri.port)
|
230
229
|
if @filesuri.scheme == "https"
|
231
|
-
# Eliminate the OpenSSL "using default DH parameters" warning
|
232
|
-
if File.exist?(File.join(@configdir, 'etch', 'dhparams'))
|
233
|
-
dh = OpenSSL::PKey::DH.new(IO.read(File.join(@configdir, 'etch', 'dhparams')))
|
234
|
-
Net::HTTP.ssl_context_accessor(:tmp_dh_callback)
|
235
|
-
http.tmp_dh_callback = proc { dh }
|
236
|
-
end
|
237
230
|
http.use_ssl = true
|
238
231
|
if File.exist?(File.join(@configdir, 'etch', 'ca.pem'))
|
239
232
|
http.ca_file = File.join(@configdir, 'etch', 'ca.pem')
|
@@ -286,10 +279,8 @@ class Etch::Client
|
|
286
279
|
files.each do |file|
|
287
280
|
request["files[#{CGI.escape(file)}][sha1sum]"] =
|
288
281
|
get_orig_sum(file)
|
289
|
-
|
290
|
-
|
291
|
-
request["files[#{CGI.escape(file)}][local_requests]"] =
|
292
|
-
local_requests
|
282
|
+
get_local_requests(file).each_with_index do |lr, i|
|
283
|
+
request["files[#{CGI.escape(file)}][local_requests][#{i}]"] = lr
|
293
284
|
end
|
294
285
|
end
|
295
286
|
end
|
@@ -325,84 +316,43 @@ class Etch::Client
|
|
325
316
|
# Send request to server
|
326
317
|
#
|
327
318
|
|
328
|
-
responsedata = {}
|
329
319
|
if @local
|
330
|
-
|
331
|
-
# FIXME: Etch#generate returns parsed XML using whatever XML
|
332
|
-
# library it happens to use. In order to avoid re-parsing
|
333
|
-
# the XML we'd have to use the XML abstraction code from Etch
|
334
|
-
# everwhere here.
|
335
|
-
# Until then re-parse the XML using REXML.
|
336
|
-
#responsedata[:configs] = results[:configs]
|
337
|
-
responsedata[:configs] = {}
|
338
|
-
results[:configs].each {|f,c| responsedata[:configs][f] = REXML::Document.new(c.to_s) }
|
339
|
-
responsedata[:need_sums] = {}
|
340
|
-
responsedata[:need_origs] = results[:need_orig]
|
341
|
-
#responsedata[:allcommands] = results[:allcommands]
|
342
|
-
responsedata[:allcommands] = {}
|
343
|
-
results[:allcommands].each {|cn,c| responsedata[:allcommands][cn] = REXML::Document.new(c.to_s) }
|
344
|
-
responsedata[:retrycommands] = results[:retrycommands]
|
320
|
+
response = @etch.generate(@local, @facts, request)
|
345
321
|
else
|
346
322
|
puts "Sending request to server #{@filesuri}: #{request.inspect}" if (@debug)
|
347
323
|
post = Net::HTTP::Post.new(@filesuri.path)
|
348
324
|
post.set_form_data(request)
|
325
|
+
post['Accept'] = 'application/x-yaml'
|
349
326
|
sign_post!(post, @key)
|
350
|
-
|
351
|
-
if !
|
352
|
-
$stderr.puts
|
327
|
+
httpresponse = http.request(post)
|
328
|
+
if !httpresponse.kind_of?(Net::HTTPSuccess)
|
329
|
+
$stderr.puts httpresponse.body
|
353
330
|
# error! raises an exception
|
354
|
-
|
355
|
-
end
|
356
|
-
puts "Response from server:\n'#{
|
357
|
-
if
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
file = config.attributes['filename']
|
362
|
-
# We have to make a new document so that XPath paths are
|
363
|
-
# referenced relative to the configuration for this
|
364
|
-
# specific file.
|
365
|
-
#responsedata[:configs][file] = REXML::Document.new(response_xml.elements["/files/configs/config[@filename='#{file}']"].to_s)
|
366
|
-
responsedata[:configs][file] = REXML::Document.new(config.to_s)
|
367
|
-
end
|
368
|
-
responsedata[:need_sums] = {}
|
369
|
-
response_xml.elements.each('/files/need_sums/need_sum') do |ns|
|
370
|
-
responsedata[:need_sums][ns.text] = true
|
371
|
-
end
|
372
|
-
responsedata[:need_origs] = {}
|
373
|
-
response_xml.elements.each('/files/need_origs/need_orig') do |no|
|
374
|
-
responsedata[:need_origs][no.text] = true
|
375
|
-
end
|
376
|
-
responsedata[:allcommands] = {}
|
377
|
-
response_xml.elements.each('/files/allcommands/commands') do |command|
|
378
|
-
commandname = command.attributes['commandname']
|
379
|
-
# We have to make a new document so that XPath paths are
|
380
|
-
# referenced relative to the configuration for this
|
381
|
-
# specific file.
|
382
|
-
#responsedata[:allcommands][commandname] = REXML::Document.new(response_xml.root.elements["/files/allcommands/commands[@commandname='#{commandname}']"].to_s)
|
383
|
-
responsedata[:allcommands][commandname] = REXML::Document.new(command.to_s)
|
384
|
-
end
|
385
|
-
responsedata[:retrycommands] = {}
|
386
|
-
response_xml.elements.each('/files/retrycommands/retrycommand') do |rc|
|
387
|
-
responsedata[:retrycommands][rc.text] = true
|
388
|
-
end
|
389
|
-
else
|
331
|
+
httpresponse.error!
|
332
|
+
end
|
333
|
+
puts "Response from server:\n'#{httpresponse.body}'" if (@debug)
|
334
|
+
if httpresponse['Content-Type'].split(';').first != 'application/x-yaml'
|
335
|
+
raise "MIME type #{httpresponse['Content-Type']} is not yaml"
|
336
|
+
end
|
337
|
+
if httpresponse.body.nil? || httpresponse.body.empty?
|
390
338
|
puts " Response is empty" if (@debug)
|
391
339
|
break
|
392
340
|
end
|
341
|
+
response = YAML.load(httpresponse.body)
|
393
342
|
end
|
394
343
|
|
395
344
|
#
|
396
345
|
# Process the response from the server
|
397
346
|
#
|
398
347
|
|
399
|
-
# Prep a clean request hash
|
348
|
+
# Prep a clean request hash in case we need to make a
|
349
|
+
# followup request
|
400
350
|
if @local
|
401
351
|
request = {}
|
402
|
-
if !
|
352
|
+
if response[:need_orig] && !response[:need_orig].empty?
|
403
353
|
request[:files] = {}
|
404
354
|
end
|
405
|
-
if !
|
355
|
+
if response[:retrycommands] && !response[:retrycommands].empty?
|
406
356
|
request[:commands] = {}
|
407
357
|
end
|
408
358
|
else
|
@@ -418,10 +368,10 @@ class Etch::Client
|
|
418
368
|
reset_already_processed
|
419
369
|
# Process configs first, as they may contain setup entries that are
|
420
370
|
# needed to create the original files.
|
421
|
-
|
371
|
+
response[:configs].each_key do |file|
|
422
372
|
puts "Processing config for #{file}" if (@debug)
|
423
373
|
if !@listfiles
|
424
|
-
continue_processing = process_file(file,
|
374
|
+
continue_processing = process_file(file, response)
|
425
375
|
if !continue_processing
|
426
376
|
throw :stop_processing
|
427
377
|
end
|
@@ -429,7 +379,7 @@ class Etch::Client
|
|
429
379
|
files_to_list[file] = true
|
430
380
|
end
|
431
381
|
end
|
432
|
-
|
382
|
+
response[:need_sum] && response[:need_sum].each do |need_sum|
|
433
383
|
puts "Processing request for sum of #{need_sum}" if (@debug)
|
434
384
|
if @local
|
435
385
|
# If this happens we screwed something up, the local mode
|
@@ -444,13 +394,14 @@ class Etch::Client
|
|
444
394
|
if @local
|
445
395
|
request[:files][need_sum][:local_requests] = local_requests
|
446
396
|
else
|
447
|
-
|
448
|
-
local_requests
|
397
|
+
local_requests.each_with_index do |lr, i|
|
398
|
+
request["files[#{CGI.escape(need_sum)}][local_requests][#{i}]"] = lr
|
399
|
+
end
|
449
400
|
end
|
450
401
|
end
|
451
402
|
need_to_loop = true
|
452
403
|
end
|
453
|
-
|
404
|
+
response[:need_orig] && response[:need_orig].each do |need_orig|
|
454
405
|
puts "Processing request for contents of #{need_orig}" if (@debug)
|
455
406
|
if @local
|
456
407
|
request[:files][need_orig] = {:orig => save_orig(need_orig)}
|
@@ -465,20 +416,21 @@ class Etch::Client
|
|
465
416
|
if @local
|
466
417
|
request[:files][need_orig][:local_requests] = local_requests
|
467
418
|
else
|
468
|
-
|
469
|
-
local_requests
|
419
|
+
local_requests.each_with_index do |lr, i|
|
420
|
+
request["files[#{CGI.escape(need_orig)}][local_requests][#{i}]"] = lr
|
421
|
+
end
|
470
422
|
end
|
471
423
|
end
|
472
424
|
need_to_loop = true
|
473
425
|
end
|
474
|
-
|
426
|
+
response[:commands] && response[:commands].each_key do |commandname|
|
475
427
|
puts "Processing commands #{commandname}" if (@debug)
|
476
|
-
continue_processing = process_commands(commandname,
|
428
|
+
continue_processing = process_commands(commandname, response)
|
477
429
|
if !continue_processing
|
478
430
|
throw :stop_processing
|
479
431
|
end
|
480
432
|
end
|
481
|
-
|
433
|
+
response[:retrycommands] && response[:retrycommands].each_key do |commandname|
|
482
434
|
puts "Processing request to retry command #{commandname}" if (@debug)
|
483
435
|
if @local
|
484
436
|
request[:commands][commandname] = true
|
@@ -598,7 +550,7 @@ class Etch::Client
|
|
598
550
|
# Raises an exception if any fatal error is encountered
|
599
551
|
# Returns a boolean, true unless the user indicated in interactive mode
|
600
552
|
# that further processing should be halted
|
601
|
-
def process_file(file,
|
553
|
+
def process_file(file, response)
|
602
554
|
continue_processing = true
|
603
555
|
save_results = true
|
604
556
|
exception = nil
|
@@ -606,7 +558,7 @@ class Etch::Client
|
|
606
558
|
# We may not have configuration for this file, if it does not apply
|
607
559
|
# to this host. The server takes care of detecting any errors that
|
608
560
|
# might involve, so here we can just silently return.
|
609
|
-
config =
|
561
|
+
config = response[:configs][file]
|
610
562
|
if !config
|
611
563
|
puts "No configuration for #{file}, skipping" if (@debug)
|
612
564
|
return continue_processing
|
@@ -651,18 +603,18 @@ class Etch::Client
|
|
651
603
|
lock_file(file)
|
652
604
|
|
653
605
|
# Process any other files that this file depends on
|
654
|
-
config.
|
655
|
-
puts "Processing dependency #{depend
|
656
|
-
continue_processing = process_file(depend
|
606
|
+
config[:depend] && config[:depend].each do |depend|
|
607
|
+
puts "Processing dependency #{depend}" if (@debug)
|
608
|
+
continue_processing = process_file(depend, response)
|
657
609
|
if !continue_processing
|
658
610
|
throw :process_done
|
659
611
|
end
|
660
612
|
end
|
661
613
|
|
662
614
|
# Process any commands that this file depends on
|
663
|
-
config.
|
664
|
-
puts "Processing command dependency #{dependcommand
|
665
|
-
continue_processing = process_commands(dependcommand
|
615
|
+
config[:dependcommand] && config[:dependcommand].each do |dependcommand|
|
616
|
+
puts "Processing command dependency #{dependcommand}" if (@debug)
|
617
|
+
continue_processing = process_commands(dependcommand, response)
|
666
618
|
if !continue_processing
|
667
619
|
throw :process_done
|
668
620
|
end
|
@@ -672,7 +624,7 @@ class Etch::Client
|
|
672
624
|
|
673
625
|
# Check to see if the user has requested that we revert back to the
|
674
626
|
# original file.
|
675
|
-
if config
|
627
|
+
if config[:revert]
|
676
628
|
origpathbase = File.join(@origbase, file)
|
677
629
|
origpath = nil
|
678
630
|
|
@@ -729,20 +681,18 @@ class Etch::Client
|
|
729
681
|
# install a package containing a sample config file which we
|
730
682
|
# then edit with a script, and thus doing the install in <pre>
|
731
683
|
# is too late.
|
732
|
-
|
733
|
-
process_setup(file, config)
|
734
|
-
end
|
684
|
+
process_setup(file, config)
|
735
685
|
|
736
|
-
if config
|
686
|
+
if config[:file] # Regular file
|
737
687
|
newcontents = nil
|
738
|
-
if config
|
739
|
-
newcontents = Base64.decode64(config
|
688
|
+
if config[:file][:contents]
|
689
|
+
newcontents = Base64.decode64(config[:file][:contents])
|
740
690
|
end
|
741
691
|
|
742
|
-
permstring = config
|
692
|
+
permstring = config[:file][:perms].to_s
|
743
693
|
perms = permstring.oct
|
744
|
-
owner = config
|
745
|
-
group = config
|
694
|
+
owner = config[:file][:owner]
|
695
|
+
group = config[:file][:group]
|
746
696
|
uid = lookup_uid(owner)
|
747
697
|
gid = lookup_gid(group)
|
748
698
|
|
@@ -804,7 +754,12 @@ class Etch::Client
|
|
804
754
|
# Default is to show a diff of the current file and the
|
805
755
|
# newly generated file.
|
806
756
|
puts "Will make the following changes to #{file}, diff -c:"
|
807
|
-
tempfile =
|
757
|
+
tempfile = nil
|
758
|
+
if RUBY_VERSION.split('.')[0..1].join('.').to_f >= 1.9
|
759
|
+
tempfile = Tempfile.new(File.basename(file), :encoding => 'ASCII-8BIT')
|
760
|
+
else
|
761
|
+
tempfile = Tempfile.new(File.basename(file))
|
762
|
+
end
|
808
763
|
tempfile.write(newcontents)
|
809
764
|
tempfile.close
|
810
765
|
puts "============================================="
|
@@ -848,9 +803,7 @@ class Etch::Client
|
|
848
803
|
end
|
849
804
|
|
850
805
|
# Perform any pre-action commands that the user has requested
|
851
|
-
|
852
|
-
process_pre(file, config)
|
853
|
-
end
|
806
|
+
process_pre(file, config)
|
854
807
|
|
855
808
|
# If the original "file" is a directory and the user hasn't
|
856
809
|
# specifically told us we can overwrite it then raise an exception.
|
@@ -860,7 +813,7 @@ class Etch::Client
|
|
860
813
|
# originals which are directories. So we don't check until
|
861
814
|
# after any pre commands are run.
|
862
815
|
if File.directory?(file) && !File.symlink?(file) &&
|
863
|
-
!config
|
816
|
+
!config[:file][:overwrite_directory]
|
864
817
|
raise "Can't proceed, original of #{file} is a directory,\n" +
|
865
818
|
" consider the overwrite_directory flag if appropriate."
|
866
819
|
end
|
@@ -883,8 +836,7 @@ class Etch::Client
|
|
883
836
|
# only use the backup to roll back if the test fails), so don't
|
884
837
|
# bother to create a backup unless there is a test command defined.
|
885
838
|
backup = nil
|
886
|
-
if config
|
887
|
-
config.elements['/config/test']
|
839
|
+
if config[:test_before_post] || config[:test]
|
888
840
|
backup = make_backup(file)
|
889
841
|
puts "Created backup #{backup}"
|
890
842
|
end
|
@@ -896,7 +848,12 @@ class Etch::Client
|
|
896
848
|
# Write out the new contents into a temporary file
|
897
849
|
filebase = File.basename(file)
|
898
850
|
filedir = File.dirname(file)
|
899
|
-
newfile =
|
851
|
+
newfile = nil
|
852
|
+
if RUBY_VERSION.split('.')[0..1].join('.').to_f >= 1.9
|
853
|
+
newfile = Tempfile.new(filebase, filedir, :encoding => 'ASCII-8BIT')
|
854
|
+
else
|
855
|
+
newfile = Tempfile.new(filebase, filedir)
|
856
|
+
end
|
900
857
|
|
901
858
|
# Set the proper permissions on the file before putting
|
902
859
|
# data into it.
|
@@ -944,33 +901,26 @@ class Etch::Client
|
|
944
901
|
end
|
945
902
|
|
946
903
|
# Perform any test_before_post commands that the user has requested
|
947
|
-
if config
|
948
|
-
|
949
|
-
|
950
|
-
raise "test_before_post failed"
|
951
|
-
end
|
904
|
+
if !process_test_before_post(file, config)
|
905
|
+
restore_backup(file, backup)
|
906
|
+
raise "test_before_post failed"
|
952
907
|
end
|
953
908
|
|
954
909
|
# Perform any post-action commands that the user has requested
|
955
|
-
|
956
|
-
process_post(file, config)
|
957
|
-
end
|
910
|
+
process_post(file, config)
|
958
911
|
|
959
912
|
# Perform any test commands that the user has requested
|
960
|
-
if config
|
913
|
+
if config[:test]
|
961
914
|
if !process_test(file, config)
|
962
915
|
restore_backup(file, backup)
|
963
916
|
|
964
917
|
# Re-run any post commands
|
965
|
-
|
966
|
-
process_post(file, config)
|
967
|
-
end
|
918
|
+
process_post(file, config)
|
968
919
|
end
|
969
920
|
end
|
970
921
|
|
971
922
|
# Clean up the backup, we don't need it anymore
|
972
|
-
if config
|
973
|
-
config.elements['/config/test']
|
923
|
+
if config[:test_before_post] || config[:test]
|
974
924
|
puts "Removing backup #{backup}"
|
975
925
|
remove_file(backup) if (!@dryrun)
|
976
926
|
end
|
@@ -982,29 +932,29 @@ class Etch::Client
|
|
982
932
|
end
|
983
933
|
end
|
984
934
|
|
985
|
-
if config
|
935
|
+
if config[:link] # Symbolic link
|
986
936
|
|
987
|
-
dest = config
|
937
|
+
dest = config[:link][:dest]
|
988
938
|
|
989
939
|
set_link_destination = !compare_link_destination(file, dest)
|
990
940
|
absdest = File.expand_path(dest, File.dirname(file))
|
991
941
|
|
992
|
-
permstring = config
|
942
|
+
permstring = config[:link][:perms].to_s
|
993
943
|
perms = permstring.oct
|
994
|
-
owner = config
|
995
|
-
group = config
|
944
|
+
owner = config[:link][:owner]
|
945
|
+
group = config[:link][:group]
|
996
946
|
uid = lookup_uid(owner)
|
997
947
|
gid = lookup_gid(group)
|
998
948
|
|
999
949
|
# lchown and lchmod are not supported on many platforms. The server
|
1000
950
|
# always includes ownership and permissions settings with any link
|
1001
|
-
# (pulling them from defaults
|
1002
|
-
# the config
|
951
|
+
# (pulling them from defaults if the user didn't specify them in
|
952
|
+
# the config file.) As such link management would always fail
|
1003
953
|
# on systems which don't support lchown/lchmod, which seems like bad
|
1004
954
|
# behavior. So instead we check to see if they are implemented, and
|
1005
955
|
# if not just ignore ownership/permissions settings. I suppose the
|
1006
956
|
# ideal would be for the server to tell the client whether the
|
1007
|
-
# ownership/permissions were specifically requested (in config
|
957
|
+
# ownership/permissions were specifically requested (in the config)
|
1008
958
|
# rather than just defaults, and then for the client to always try to
|
1009
959
|
# manage ownership/permissions if the settings are not defaults (and
|
1010
960
|
# fail in the event that they aren't implemented.)
|
@@ -1077,7 +1027,7 @@ class Etch::Client
|
|
1077
1027
|
# expand_path should handle paths that are already absolute
|
1078
1028
|
# properly.
|
1079
1029
|
elsif ! File.exist?(absdest) && ! File.symlink?(absdest) &&
|
1080
|
-
! config
|
1030
|
+
! config[:link][:allow_nonexistent_dest]
|
1081
1031
|
puts "Destination #{dest} for link #{file} does not exist," +
|
1082
1032
|
" consider the allow_nonexistent_dest flag if appropriate."
|
1083
1033
|
throw :process_done
|
@@ -1112,9 +1062,7 @@ class Etch::Client
|
|
1112
1062
|
end
|
1113
1063
|
|
1114
1064
|
# Perform any pre-action commands that the user has requested
|
1115
|
-
|
1116
|
-
process_pre(file, config)
|
1117
|
-
end
|
1065
|
+
process_pre(file, config)
|
1118
1066
|
|
1119
1067
|
# If the original "file" is a directory and the user hasn't
|
1120
1068
|
# specifically told us we can overwrite it then raise an exception.
|
@@ -1124,7 +1072,7 @@ class Etch::Client
|
|
1124
1072
|
# originals which are directories. So we don't check until
|
1125
1073
|
# after any pre commands are run.
|
1126
1074
|
if File.directory?(file) && !File.symlink?(file) &&
|
1127
|
-
!config
|
1075
|
+
!config[:link][:overwrite_directory]
|
1128
1076
|
raise "Can't proceed, original of #{file} is a directory,\n" +
|
1129
1077
|
" consider the overwrite_directory flag if appropriate."
|
1130
1078
|
end
|
@@ -1147,8 +1095,7 @@ class Etch::Client
|
|
1147
1095
|
# only use the backup to roll back if the test fails), so don't
|
1148
1096
|
# bother to create a backup unless there is a test command defined.
|
1149
1097
|
backup = nil
|
1150
|
-
if config
|
1151
|
-
config.elements['/config/test']
|
1098
|
+
if config[:test_before_post] || config[:test]
|
1152
1099
|
backup = make_backup(file)
|
1153
1100
|
puts "Created backup #{backup}"
|
1154
1101
|
end
|
@@ -1185,33 +1132,26 @@ class Etch::Client
|
|
1185
1132
|
end
|
1186
1133
|
|
1187
1134
|
# Perform any test_before_post commands that the user has requested
|
1188
|
-
if config
|
1189
|
-
|
1190
|
-
|
1191
|
-
raise "test_before_post failed"
|
1192
|
-
end
|
1135
|
+
if !process_test_before_post(file, config)
|
1136
|
+
restore_backup(file, backup)
|
1137
|
+
raise "test_before_post failed"
|
1193
1138
|
end
|
1194
1139
|
|
1195
1140
|
# Perform any post-action commands that the user has requested
|
1196
|
-
|
1197
|
-
process_post(file, config)
|
1198
|
-
end
|
1141
|
+
process_post(file, config)
|
1199
1142
|
|
1200
1143
|
# Perform any test commands that the user has requested
|
1201
|
-
if config
|
1144
|
+
if config[:test]
|
1202
1145
|
if !process_test(file, config)
|
1203
1146
|
restore_backup(file, backup)
|
1204
1147
|
|
1205
1148
|
# Re-run any post commands
|
1206
|
-
|
1207
|
-
process_post(file, config)
|
1208
|
-
end
|
1149
|
+
process_post(file, config)
|
1209
1150
|
end
|
1210
1151
|
end
|
1211
1152
|
|
1212
1153
|
# Clean up the backup, we don't need it anymore
|
1213
|
-
if config
|
1214
|
-
config.elements['/config/test']
|
1154
|
+
if config[:test_before_post] || config[:test]
|
1215
1155
|
puts "Removing backup #{backup}"
|
1216
1156
|
remove_file(backup) if (!@dryrun)
|
1217
1157
|
end
|
@@ -1223,16 +1163,17 @@ class Etch::Client
|
|
1223
1163
|
end
|
1224
1164
|
end
|
1225
1165
|
|
1226
|
-
if config
|
1166
|
+
if config[:directory] # Directory
|
1227
1167
|
|
1228
1168
|
# A little safety check
|
1229
|
-
|
1230
|
-
|
1169
|
+
if !config[:directory][:create]
|
1170
|
+
raise "No create element found in directory section"
|
1171
|
+
end
|
1231
1172
|
|
1232
|
-
permstring = config
|
1173
|
+
permstring = config[:directory][:perms].to_s
|
1233
1174
|
perms = permstring.oct
|
1234
|
-
owner = config
|
1235
|
-
group = config
|
1175
|
+
owner = config[:directory][:owner]
|
1176
|
+
group = config[:directory][:group]
|
1236
1177
|
uid = lookup_uid(owner)
|
1237
1178
|
gid = lookup_gid(group)
|
1238
1179
|
|
@@ -1291,9 +1232,7 @@ class Etch::Client
|
|
1291
1232
|
end
|
1292
1233
|
|
1293
1234
|
# Perform any pre-action commands that the user has requested
|
1294
|
-
|
1295
|
-
process_pre(file, config)
|
1296
|
-
end
|
1235
|
+
process_pre(file, config)
|
1297
1236
|
|
1298
1237
|
# Give save_orig a definitive answer on whether or not to save the
|
1299
1238
|
# contents of an original directory.
|
@@ -1313,8 +1252,7 @@ class Etch::Client
|
|
1313
1252
|
# only use the backup to roll back if the test fails), so don't
|
1314
1253
|
# bother to create a backup unless there is a test command defined.
|
1315
1254
|
backup = nil
|
1316
|
-
if config
|
1317
|
-
config.elements['/config/test']
|
1255
|
+
if config[:test_before_post] || config[:test]
|
1318
1256
|
backup = make_backup(file)
|
1319
1257
|
puts "Created backup #{backup}"
|
1320
1258
|
end
|
@@ -1345,33 +1283,26 @@ class Etch::Client
|
|
1345
1283
|
end
|
1346
1284
|
|
1347
1285
|
# Perform any test_before_post commands that the user has requested
|
1348
|
-
if config
|
1349
|
-
|
1350
|
-
|
1351
|
-
raise "test_before_post failed"
|
1352
|
-
end
|
1286
|
+
if !process_test_before_post(file, config)
|
1287
|
+
restore_backup(file, backup)
|
1288
|
+
raise "test_before_post failed"
|
1353
1289
|
end
|
1354
1290
|
|
1355
1291
|
# Perform any post-action commands that the user has requested
|
1356
|
-
|
1357
|
-
process_post(file, config)
|
1358
|
-
end
|
1292
|
+
process_post(file, config)
|
1359
1293
|
|
1360
1294
|
# Perform any test commands that the user has requested
|
1361
|
-
if config
|
1295
|
+
if config[:test]
|
1362
1296
|
if !process_test(file, config)
|
1363
1297
|
restore_backup(file, backup)
|
1364
1298
|
|
1365
1299
|
# Re-run any post commands
|
1366
|
-
|
1367
|
-
process_post(file, config)
|
1368
|
-
end
|
1300
|
+
process_post(file, config)
|
1369
1301
|
end
|
1370
1302
|
end
|
1371
1303
|
|
1372
1304
|
# Clean up the backup, we don't need it anymore
|
1373
|
-
if config
|
1374
|
-
config.elements['/config/test']
|
1305
|
+
if config[:test_before_post] || config[:test]
|
1375
1306
|
puts "Removing backup #{backup}"
|
1376
1307
|
remove_file(backup) if (!@dryrun)
|
1377
1308
|
end
|
@@ -1383,11 +1314,12 @@ class Etch::Client
|
|
1383
1314
|
end
|
1384
1315
|
end
|
1385
1316
|
|
1386
|
-
if config
|
1317
|
+
if config[:delete] # Delete whatever is there
|
1387
1318
|
|
1388
1319
|
# A little safety check
|
1389
|
-
|
1390
|
-
|
1320
|
+
if !config[:delete][:proceed]
|
1321
|
+
raise "No proceed element found in delete section"
|
1322
|
+
end
|
1391
1323
|
|
1392
1324
|
# Proceed only if the file currently exists
|
1393
1325
|
if !File.exist?(file) && !File.symlink?(file)
|
@@ -1415,9 +1347,7 @@ class Etch::Client
|
|
1415
1347
|
end
|
1416
1348
|
|
1417
1349
|
# Perform any pre-action commands that the user has requested
|
1418
|
-
|
1419
|
-
process_pre(file, config)
|
1420
|
-
end
|
1350
|
+
process_pre(file, config)
|
1421
1351
|
|
1422
1352
|
# If the original "file" is a directory and the user hasn't
|
1423
1353
|
# specifically told us we can overwrite it then raise an exception.
|
@@ -1427,7 +1357,7 @@ class Etch::Client
|
|
1427
1357
|
# originals which are directories. So we don't check until
|
1428
1358
|
# after any pre commands are run.
|
1429
1359
|
if File.directory?(file) && !File.symlink?(file) &&
|
1430
|
-
!config
|
1360
|
+
!config[:delete][:overwrite_directory]
|
1431
1361
|
raise "Can't proceed, original of #{file} is a directory,\n" +
|
1432
1362
|
" consider the overwrite_directory flag if appropriate."
|
1433
1363
|
end
|
@@ -1443,8 +1373,7 @@ class Etch::Client
|
|
1443
1373
|
# only use the backup to roll back if the test fails), so don't
|
1444
1374
|
# bother to create a backup unless there is a test command defined.
|
1445
1375
|
backup = nil
|
1446
|
-
if config
|
1447
|
-
config.elements['/config/test']
|
1376
|
+
if config[:test_before_post] || config[:test]
|
1448
1377
|
backup = make_backup(file)
|
1449
1378
|
puts "Created backup #{backup}"
|
1450
1379
|
end
|
@@ -1453,33 +1382,26 @@ class Etch::Client
|
|
1453
1382
|
remove_file(file) if (!@dryrun)
|
1454
1383
|
|
1455
1384
|
# Perform any test_before_post commands that the user has requested
|
1456
|
-
if config
|
1457
|
-
|
1458
|
-
|
1459
|
-
raise "test_before_post failed"
|
1460
|
-
end
|
1385
|
+
if !process_test_before_post(file, config)
|
1386
|
+
restore_backup(file, backup)
|
1387
|
+
raise "test_before_post failed"
|
1461
1388
|
end
|
1462
1389
|
|
1463
1390
|
# Perform any post-action commands that the user has requested
|
1464
|
-
|
1465
|
-
process_post(file, config)
|
1466
|
-
end
|
1391
|
+
process_post(file, config)
|
1467
1392
|
|
1468
1393
|
# Perform any test commands that the user has requested
|
1469
|
-
if config
|
1394
|
+
if config[:test]
|
1470
1395
|
if !process_test(file, config)
|
1471
1396
|
restore_backup(file, backup)
|
1472
1397
|
|
1473
1398
|
# Re-run any post commands
|
1474
|
-
|
1475
|
-
process_post(file, config)
|
1476
|
-
end
|
1399
|
+
process_post(file, config)
|
1477
1400
|
end
|
1478
1401
|
end
|
1479
1402
|
|
1480
1403
|
# Clean up the backup, we don't need it anymore
|
1481
|
-
if config
|
1482
|
-
config.elements['/config/test']
|
1404
|
+
if config[:test_before_post] || config[:test]
|
1483
1405
|
puts "Removing backup #{backup}"
|
1484
1406
|
remove_file(backup) if (!@dryrun)
|
1485
1407
|
end
|
@@ -1520,7 +1442,7 @@ class Etch::Client
|
|
1520
1442
|
# Raises an exception if any fatal error is encountered
|
1521
1443
|
# Returns a boolean, true unless the user indicated in interactive mode
|
1522
1444
|
# that further processing should be halted
|
1523
|
-
def process_commands(commandname,
|
1445
|
+
def process_commands(commandname, response)
|
1524
1446
|
continue_processing = true
|
1525
1447
|
save_results = true
|
1526
1448
|
exception = nil
|
@@ -1528,7 +1450,7 @@ class Etch::Client
|
|
1528
1450
|
# We may not have configuration for this file, if it does not apply
|
1529
1451
|
# to this host. The server takes care of detecting any errors that
|
1530
1452
|
# might involve, so here we can just silently return.
|
1531
|
-
command =
|
1453
|
+
command = response[:commands][commandname]
|
1532
1454
|
if !command
|
1533
1455
|
puts "No configuration for command #{commandname}, skipping" if (@debug)
|
1534
1456
|
return continue_processing
|
@@ -1573,60 +1495,67 @@ class Etch::Client
|
|
1573
1495
|
lock_file(commandname)
|
1574
1496
|
|
1575
1497
|
# Process any other commands that this command depends on
|
1576
|
-
command.
|
1577
|
-
puts "Processing command dependency #{depend
|
1578
|
-
continue_processing = process_commands(depend
|
1498
|
+
command[:depend] && command[:depend].each do |depend|
|
1499
|
+
puts "Processing command dependency #{depend}" if (@debug)
|
1500
|
+
continue_processing = process_commands(depend, response)
|
1579
1501
|
if !continue_processing
|
1580
1502
|
throw :process_done
|
1581
1503
|
end
|
1582
1504
|
end
|
1583
1505
|
|
1584
1506
|
# Process any files that this command depends on
|
1585
|
-
command.
|
1586
|
-
puts "Processing file dependency #{dependfile
|
1587
|
-
continue_processing = process_file(dependfile
|
1507
|
+
command[:dependfile] && command[:dependfile].each do |dependfile|
|
1508
|
+
puts "Processing file dependency #{dependfile}" if (@debug)
|
1509
|
+
continue_processing = process_file(dependfile, response)
|
1588
1510
|
if !continue_processing
|
1589
1511
|
throw :process_done
|
1590
1512
|
end
|
1591
1513
|
end
|
1592
1514
|
|
1593
1515
|
# Perform each step
|
1594
|
-
command.
|
1595
|
-
|
1596
|
-
|
1597
|
-
|
1598
|
-
|
1599
|
-
|
1516
|
+
command[:steps] && command[:steps].each do |outerstep|
|
1517
|
+
if step = outerstep[:step]
|
1518
|
+
# Run guards, display only in debug (a la setup)
|
1519
|
+
guard_result = true
|
1520
|
+
step[:guard] && step[:guard].each do |guard|
|
1521
|
+
guard_result &= process_guard(guard, commandname)
|
1522
|
+
end
|
1600
1523
|
|
1601
|
-
|
1602
|
-
|
1603
|
-
|
1524
|
+
if !guard_result
|
1525
|
+
# Tell the user what we're going to do
|
1526
|
+
puts "Will run command '#{step[:command].join('; ')}'"
|
1604
1527
|
|
1605
|
-
|
1606
|
-
|
1607
|
-
|
1608
|
-
|
1609
|
-
|
1610
|
-
|
1611
|
-
|
1612
|
-
|
1613
|
-
|
1614
|
-
|
1615
|
-
|
1616
|
-
|
1617
|
-
|
1618
|
-
|
1619
|
-
|
1528
|
+
# If the user requested interactive mode ask them for
|
1529
|
+
# confirmation to proceed.
|
1530
|
+
if @interactive
|
1531
|
+
case get_user_confirmation()
|
1532
|
+
when CONFIRM_PROCEED
|
1533
|
+
# No need to do anything
|
1534
|
+
when CONFIRM_SKIP
|
1535
|
+
save_results = false
|
1536
|
+
throw :process_done
|
1537
|
+
when CONFIRM_QUIT
|
1538
|
+
continue_processing = false
|
1539
|
+
save_results = false
|
1540
|
+
throw :process_done
|
1541
|
+
else
|
1542
|
+
raise "Unexpected result from get_user_confirmation()"
|
1543
|
+
end
|
1620
1544
|
end
|
1621
|
-
end
|
1622
1545
|
|
1623
|
-
|
1624
|
-
|
1546
|
+
# Run command, always display (a la pre/post)
|
1547
|
+
step[:command] && step[:command].each do |cmd|
|
1548
|
+
process_command(cmd, commandname)
|
1549
|
+
end
|
1625
1550
|
|
1626
|
-
|
1627
|
-
|
1628
|
-
|
1629
|
-
|
1551
|
+
# Re-run guard, always display, abort if fails
|
1552
|
+
guard_recheck_result = true
|
1553
|
+
step[:guard] && step[:guard].each do |guard|
|
1554
|
+
guard_recheck_result &= process_guard(guard, commandname)
|
1555
|
+
end
|
1556
|
+
if !guard_recheck_result
|
1557
|
+
raise "Guard #{step[:guard].join('; ')} still fails for #{commandname} after running command #{step[:command].join('; ')}"
|
1558
|
+
end
|
1630
1559
|
end
|
1631
1560
|
end
|
1632
1561
|
end
|
@@ -1663,7 +1592,14 @@ class Etch::Client
|
|
1663
1592
|
# If the file currently exists and is a regular file then check to see
|
1664
1593
|
# if the new contents are the same.
|
1665
1594
|
if File.file?(file) && !File.symlink?(file)
|
1666
|
-
|
1595
|
+
# As elsewhere we have no idea how arbitrary files that the user is
|
1596
|
+
# managing are encoded, so tell Ruby to read the file in binary mode.
|
1597
|
+
contents = nil
|
1598
|
+
if RUBY_VERSION.split('.')[0..1].join('.').to_f >= 1.9
|
1599
|
+
contents = IO.read(file, :encoding => 'ASCII-8BIT')
|
1600
|
+
else
|
1601
|
+
contents = IO.read(file)
|
1602
|
+
end
|
1667
1603
|
if newcontents == contents
|
1668
1604
|
return true
|
1669
1605
|
end
|
@@ -1696,7 +1632,14 @@ class Etch::Client
|
|
1696
1632
|
# a regular file, otherwise we send back an empty string.
|
1697
1633
|
if (origpath =~ /\.ORIG$/ || origpath =~ /\.TMP$/) &&
|
1698
1634
|
File.file?(origpath) && !File.symlink?(origpath)
|
1699
|
-
|
1635
|
+
# As elsewhere we have no idea how arbitrary files that the user is
|
1636
|
+
# managing are encoded, so tell Ruby to read the file in binary mode.
|
1637
|
+
orig_contents = nil
|
1638
|
+
if RUBY_VERSION.split('.')[0..1].join('.').to_f >= 1.9
|
1639
|
+
orig_contents = IO.read(origpath, :encoding => 'ASCII-8BIT')
|
1640
|
+
else
|
1641
|
+
orig_contents = IO.read(origpath)
|
1642
|
+
end
|
1700
1643
|
else
|
1701
1644
|
orig_contents = ''
|
1702
1645
|
end
|
@@ -1970,33 +1913,15 @@ class Etch::Client
|
|
1970
1913
|
|
1971
1914
|
def get_local_requests(file)
|
1972
1915
|
requestdir = File.join(@requestbase, file)
|
1973
|
-
|
1916
|
+
requests = []
|
1974
1917
|
if File.directory?(requestdir)
|
1975
1918
|
Dir.foreach(requestdir) do |entry|
|
1976
1919
|
next if entry == '.'
|
1977
1920
|
next if entry == '..'
|
1978
1921
|
requestfile = File.join(requestdir, entry)
|
1979
|
-
|
1980
|
-
# Make sure it is valid XML
|
1981
|
-
begin
|
1982
|
-
request_xml = REXML::Document.new(request)
|
1983
|
-
rescue REXML::ParseException => e
|
1984
|
-
warn "Local request file #{requestfile} is not valid XML and will be ignored:\n" + e.message
|
1985
|
-
next
|
1986
|
-
end
|
1987
|
-
# Make sure the root element is <request>
|
1988
|
-
if request_xml.root.name != 'request'
|
1989
|
-
warn "Local request file #{requestfile} is not properly formatted and will be ignored, XML root element is not <request>"
|
1990
|
-
next
|
1991
|
-
end
|
1992
|
-
# Add it to the queue
|
1993
|
-
requestlist << request
|
1922
|
+
requests << IO.read(requestfile)
|
1994
1923
|
end
|
1995
1924
|
end
|
1996
|
-
requests = nil
|
1997
|
-
if !requestlist.empty?
|
1998
|
-
requests = "<requests>\n#{requestlist.join('')}\n</requests>"
|
1999
|
-
end
|
2000
1925
|
requests
|
2001
1926
|
end
|
2002
1927
|
|
@@ -2064,96 +1989,105 @@ class Etch::Client
|
|
2064
1989
|
end
|
2065
1990
|
|
2066
1991
|
def process_setup(file, config)
|
2067
|
-
|
2068
|
-
|
2069
|
-
|
2070
|
-
|
2071
|
-
|
2072
|
-
|
2073
|
-
|
2074
|
-
|
2075
|
-
|
2076
|
-
|
2077
|
-
|
1992
|
+
if config[:setup]
|
1993
|
+
# Because the setup commands are processed every time etch runs
|
1994
|
+
# (rather than just when the file has changed, as with pre/post) we
|
1995
|
+
# don't want to print a message for them unless we're in debug mode.
|
1996
|
+
puts "Processing setup commands" if (@debug)
|
1997
|
+
config[:setup].each do |setup|
|
1998
|
+
r = process_exec(:setup, setup, file)
|
1999
|
+
# process_exec currently raises an exception if a setup or pre command
|
2000
|
+
# fails. In case that ever changes make sure we propagate
|
2001
|
+
# the error.
|
2002
|
+
return r if (!r)
|
2003
|
+
end
|
2078
2004
|
end
|
2005
|
+
true
|
2079
2006
|
end
|
2080
2007
|
def process_pre(file, config)
|
2081
|
-
|
2082
|
-
|
2083
|
-
|
2084
|
-
|
2085
|
-
|
2086
|
-
|
2087
|
-
|
2088
|
-
|
2008
|
+
if config[:pre]
|
2009
|
+
puts "Processing pre commands"
|
2010
|
+
config[:pre].each do |pre|
|
2011
|
+
r = process_exec(:pre, pre, file)
|
2012
|
+
# process_exec currently raises an exception if a setup or pre command
|
2013
|
+
# fails. In case that ever changes make sure we propagate
|
2014
|
+
# the error.
|
2015
|
+
return r if (!r)
|
2016
|
+
end
|
2089
2017
|
end
|
2018
|
+
true
|
2090
2019
|
end
|
2091
2020
|
def process_post(file, config)
|
2092
|
-
exectype = 'post'
|
2093
2021
|
execs = []
|
2094
|
-
puts "Processing #{exectype} commands"
|
2095
2022
|
|
2096
|
-
|
2023
|
+
if [:post, :post_once, :post_once_per_run].any?{|p| config[p]}
|
2024
|
+
puts "Processing post commands"
|
2025
|
+
end
|
2026
|
+
|
2027
|
+
# Add the "post once" items into the list of commands to process
|
2097
2028
|
# if this is the first time etch has updated this file, and if
|
2098
2029
|
# we haven't already run the command.
|
2099
|
-
if @first_update[file]
|
2100
|
-
config.
|
2101
|
-
if !@exec_already_processed.has_key?(
|
2102
|
-
execs <<
|
2103
|
-
@exec_already_processed[
|
2030
|
+
if @first_update[file] && config[:post_once]
|
2031
|
+
config[:post_once].each do |once|
|
2032
|
+
if !@exec_already_processed.has_key?(once)
|
2033
|
+
execs << once
|
2034
|
+
@exec_already_processed[once] = true
|
2104
2035
|
else
|
2105
|
-
puts "Skipping '#{
|
2036
|
+
puts "Skipping '#{once}', it has already " +
|
2106
2037
|
"been executed once this run" if (@debug)
|
2107
2038
|
end
|
2108
2039
|
end
|
2109
2040
|
end
|
2110
2041
|
|
2111
|
-
# Add in the regular
|
2112
|
-
config
|
2113
|
-
execs
|
2042
|
+
# Add in the regular post items
|
2043
|
+
if config[:post]
|
2044
|
+
execs.concat config[:post]
|
2114
2045
|
end
|
2115
2046
|
|
2116
2047
|
# post failures are considered non-fatal, so we ignore the
|
2117
2048
|
# return value from process_exec (it takes care of warning
|
2118
2049
|
# the user).
|
2119
|
-
execs.each { |exec| process_exec(
|
2050
|
+
execs.each { |exec| process_exec(:post, exec, file) }
|
2120
2051
|
|
2121
|
-
config
|
2122
|
-
|
2123
|
-
|
2124
|
-
|
2125
|
-
|
2052
|
+
if config[:post_once_per_run]
|
2053
|
+
config[:post_once_per_run].each do |popr|
|
2054
|
+
# Stuff the "post once per run" nodes into the global hash to
|
2055
|
+
# be run after we've processed all files.
|
2056
|
+
puts "Adding '#{popr}' to 'post once per run' list" if (@debug)
|
2057
|
+
@exec_once_per_run[popr] = true
|
2058
|
+
end
|
2126
2059
|
end
|
2127
2060
|
end
|
2128
2061
|
def process_test_before_post(file, config)
|
2129
|
-
|
2130
|
-
|
2131
|
-
|
2132
|
-
|
2133
|
-
|
2134
|
-
|
2062
|
+
if config[:test_before_post]
|
2063
|
+
puts "Processing test_before_post commands"
|
2064
|
+
config[:test_before_post].each do |tbp|
|
2065
|
+
r = process_exec(:test_before_post, tbp, file)
|
2066
|
+
# If the test failed we need to propagate that error
|
2067
|
+
return r if (!r)
|
2068
|
+
end
|
2135
2069
|
end
|
2070
|
+
true
|
2136
2071
|
end
|
2137
2072
|
def process_test(file, config)
|
2138
|
-
|
2139
|
-
|
2140
|
-
|
2141
|
-
|
2142
|
-
|
2143
|
-
|
2073
|
+
if config[:test]
|
2074
|
+
puts "Processing test commands"
|
2075
|
+
config[:test].each do |test|
|
2076
|
+
r = process_exec(:test, test, file)
|
2077
|
+
# If the test failed we need to propagate that error
|
2078
|
+
return r if (!r)
|
2079
|
+
end
|
2144
2080
|
end
|
2145
2081
|
end
|
2146
2082
|
def process_guard(guard, commandname)
|
2147
|
-
exectype = 'guard'
|
2148
2083
|
# Because the guard commands are processed every time etch runs we don't
|
2149
2084
|
# want to print a message for them unless we're in debug mode.
|
2150
|
-
puts "Processing
|
2151
|
-
process_exec(
|
2085
|
+
puts "Processing guard" if (@debug)
|
2086
|
+
process_exec(:guard, guard, commandname)
|
2152
2087
|
end
|
2153
2088
|
def process_command(command, commandname)
|
2154
|
-
|
2155
|
-
|
2156
|
-
process_exec(exectype, command, commandname)
|
2089
|
+
puts "Processing command"
|
2090
|
+
process_exec(:command, command, commandname)
|
2157
2091
|
end
|
2158
2092
|
|
2159
2093
|
def process_exec(exectype, exec, file='')
|
@@ -2162,14 +2096,14 @@ class Etch::Client
|
|
2162
2096
|
# Because the setup and guard commands are processed every time (rather
|
2163
2097
|
# than just when the file has changed as with pre/post) we don't want to
|
2164
2098
|
# print a message for them.
|
2165
|
-
puts " Executing '#{exec}'" if (
|
2099
|
+
puts " Executing '#{exec}'" if (![:setup, :guard].include?(exectype) || @debug)
|
2166
2100
|
|
2167
2101
|
# Actually run the command unless we're in a dry run, or if we're in
|
2168
2102
|
# a damp run and the command is a setup command.
|
2169
|
-
if ! @dryrun || (@dryrun == 'damp' && exectype ==
|
2103
|
+
if ! @dryrun || exectype == :guard || (@dryrun == 'damp' && exectype == :setup)
|
2170
2104
|
etch_priority = nil
|
2171
2105
|
|
2172
|
-
if
|
2106
|
+
if [:post, :command].include?(exectype)
|
2173
2107
|
# Etch is likely running at a lower priority than normal.
|
2174
2108
|
# However, we don't want to run post commands at that
|
2175
2109
|
# priority. If they restart processes (for example,
|
@@ -2189,7 +2123,7 @@ class Etch::Client
|
|
2189
2123
|
# "FOO=bar myprogram" works.
|
2190
2124
|
r = system('/bin/sh', '-c', exec)
|
2191
2125
|
|
2192
|
-
if
|
2126
|
+
if [:post, :command].include?(exectype)
|
2193
2127
|
if etch_priority != 0
|
2194
2128
|
puts " Returning priority to #{etch_priority}" if (@debug)
|
2195
2129
|
Process.setpriority(Process::PRIO_USER, 0, etch_priority)
|
@@ -2204,7 +2138,7 @@ class Etch::Client
|
|
2204
2138
|
# what's going on if it fails. So include the command in the message if
|
2205
2139
|
# there was a failure.
|
2206
2140
|
execmsg = ''
|
2207
|
-
execmsg = "'#{exec}' " if (
|
2141
|
+
execmsg = "'#{exec}' " if ([:setup, :guard].include?(exectype))
|
2208
2142
|
|
2209
2143
|
# Normally we include the filename of the file that this command
|
2210
2144
|
# is associated with in the messages we print. But for "exec once
|
@@ -2218,21 +2152,21 @@ class Etch::Client
|
|
2218
2152
|
# software prerequisites, and bad things generally happen if
|
2219
2153
|
# those software installs fail. So consider it a fatal error if
|
2220
2154
|
# that occurs.
|
2221
|
-
if
|
2155
|
+
if [:setup, :pre].include?(exectype)
|
2222
2156
|
raise " Setup/Pre command " + execmsg + filemsg +
|
2223
2157
|
"exited with non-zero value"
|
2224
2158
|
# Post commands are generally used to restart services. While
|
2225
2159
|
# it is unfortunate if they fail, there is little to be gained
|
2226
2160
|
# by having etch exit if they do so. So simply warn if a post
|
2227
2161
|
# command fails.
|
2228
|
-
elsif exectype ==
|
2162
|
+
elsif exectype == :post
|
2229
2163
|
puts " Post command " + execmsg + filemsg +
|
2230
2164
|
"exited with non-zero value"
|
2231
2165
|
# process_commands takes the appropriate action when guards and commands
|
2232
2166
|
# fail, so we just warn of any failures here.
|
2233
|
-
elsif exectype ==
|
2167
|
+
elsif exectype == :guard
|
2234
2168
|
puts " Guard " + execmsg + filemsg + "exited with non-zero value"
|
2235
|
-
elsif exectype ==
|
2169
|
+
elsif exectype == :command
|
2236
2170
|
puts " Command " + execmsg + filemsg + "exited with non-zero value"
|
2237
2171
|
# For test commands we need to warn the user and then return a
|
2238
2172
|
# value indicating the failure so that a rollback can be
|
@@ -2247,10 +2181,9 @@ class Etch::Client
|
|
2247
2181
|
end
|
2248
2182
|
|
2249
2183
|
def lookup_uid(user)
|
2250
|
-
|
2251
|
-
if user =~ /^\d+$/
|
2184
|
+
if user.to_i.to_s == user.to_s
|
2252
2185
|
# If the user was specified as a numeric UID, use it directly.
|
2253
|
-
uid = user
|
2186
|
+
uid = user.to_i
|
2254
2187
|
else
|
2255
2188
|
# Otherwise attempt to look up the username to get a UID.
|
2256
2189
|
# Default to UID 0 if the username can't be found.
|
@@ -2258,19 +2191,17 @@ class Etch::Client
|
|
2258
2191
|
pw = Etc.getpwnam(user)
|
2259
2192
|
uid = pw.uid
|
2260
2193
|
rescue ArgumentError
|
2261
|
-
puts "config
|
2194
|
+
puts "config requests user #{user}, but that user can't be found. Using UID 0." if @debug
|
2262
2195
|
uid = 0
|
2263
2196
|
end
|
2264
2197
|
end
|
2265
|
-
|
2266
|
-
uid.to_i
|
2198
|
+
uid
|
2267
2199
|
end
|
2268
2200
|
|
2269
2201
|
def lookup_gid(group)
|
2270
|
-
|
2271
|
-
if group =~ /^\d+$/
|
2202
|
+
if group.to_i.to_s == group.to_s
|
2272
2203
|
# If the group was specified as a numeric GID, use it directly.
|
2273
|
-
gid = group
|
2204
|
+
gid = group.to_i
|
2274
2205
|
else
|
2275
2206
|
# Otherwise attempt to look up the group to get a GID. Default
|
2276
2207
|
# to GID 0 if the group can't be found.
|
@@ -2278,12 +2209,11 @@ class Etch::Client
|
|
2278
2209
|
gr = Etc.getgrnam(group)
|
2279
2210
|
gid = gr.gid
|
2280
2211
|
rescue ArgumentError
|
2281
|
-
puts "config
|
2212
|
+
puts "config requests group #{group}, but that group can't be found. Using GID 0." if @debug
|
2282
2213
|
gid = 0
|
2283
2214
|
end
|
2284
2215
|
end
|
2285
|
-
|
2286
|
-
gid.to_i
|
2216
|
+
gid
|
2287
2217
|
end
|
2288
2218
|
|
2289
2219
|
# Returns true if the permissions of the given file match the given
|