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.
Files changed (5) hide show
  1. checksums.yaml +7 -0
  2. data/Rakefile +7 -8
  3. data/lib/etch.rb +959 -399
  4. data/lib/etch/client.rb +265 -335
  5. metadata +12 -16
@@ -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 = '4.0.0'
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
- local_requests = get_local_requests(file)
290
- if local_requests
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
- results = @etch.generate(@local, @facts, request)
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
- response = http.request(post)
351
- if !response.kind_of?(Net::HTTPSuccess)
352
- $stderr.puts response.body
327
+ httpresponse = http.request(post)
328
+ if !httpresponse.kind_of?(Net::HTTPSuccess)
329
+ $stderr.puts httpresponse.body
353
330
  # error! raises an exception
354
- response.error!
355
- end
356
- puts "Response from server:\n'#{response.body}'" if (@debug)
357
- if !response.body.nil? && !response.body.empty?
358
- response_xml = REXML::Document.new(response.body)
359
- responsedata[:configs] = {}
360
- response_xml.elements.each('/files/configs/config') do |config|
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 !responsedata[:need_origs].empty?
352
+ if response[:need_orig] && !response[:need_orig].empty?
403
353
  request[:files] = {}
404
354
  end
405
- if !responsedata[:retrycommands].empty?
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
- responsedata[:configs].each_key do |file|
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, responsedata)
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
- responsedata[:need_sums].each_key do |need_sum|
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
- request["files[#{CGI.escape(need_sum)}][local_requests]"] =
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
- responsedata[:need_origs].each_key do |need_orig|
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
- request["files[#{CGI.escape(need_orig)}][local_requests]"] =
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
- responsedata[:allcommands].each_key do |commandname|
426
+ response[:commands] && response[:commands].each_key do |commandname|
475
427
  puts "Processing commands #{commandname}" if (@debug)
476
- continue_processing = process_commands(commandname, responsedata)
428
+ continue_processing = process_commands(commandname, response)
477
429
  if !continue_processing
478
430
  throw :stop_processing
479
431
  end
480
432
  end
481
- responsedata[:retrycommands].each_key do |commandname|
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, responsedata)
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 = responsedata[:configs][file]
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.elements.each('/config/depend') do |depend|
655
- puts "Processing dependency #{depend.text}" if (@debug)
656
- continue_processing = process_file(depend.text, responsedata)
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.elements.each('/config/dependcommand') do |dependcommand|
664
- puts "Processing command dependency #{dependcommand.text}" if (@debug)
665
- continue_processing = process_commands(dependcommand.text, responsedata)
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.elements['/config/revert']
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
- if config.elements['/config/setup']
733
- process_setup(file, config)
734
- end
684
+ process_setup(file, config)
735
685
 
736
- if config.elements['/config/file'] # Regular file
686
+ if config[:file] # Regular file
737
687
  newcontents = nil
738
- if config.elements['/config/file/contents']
739
- newcontents = Base64.decode64(config.elements['/config/file/contents'].text)
688
+ if config[:file][:contents]
689
+ newcontents = Base64.decode64(config[:file][:contents])
740
690
  end
741
691
 
742
- permstring = config.elements['/config/file/perms'].text
692
+ permstring = config[:file][:perms].to_s
743
693
  perms = permstring.oct
744
- owner = config.elements['/config/file/owner'].text
745
- group = config.elements['/config/file/group'].text
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 = Tempfile.new(File.basename(file))
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
- if config.elements['/config/pre']
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.elements['/config/file/overwrite_directory']
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.elements['/config/test_before_post'] ||
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 = Tempfile.new(filebase, filedir)
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.elements['/config/test_before_post']
948
- if !process_test_before_post(file, config)
949
- restore_backup(file, backup)
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
- if config.elements['/config/post']
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.elements['/config/test']
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
- if config.elements['/config/post']
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.elements['/config/test_before_post'] ||
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.elements['/config/link'] # Symbolic link
935
+ if config[:link] # Symbolic link
986
936
 
987
- dest = config.elements['/config/link/dest'].text
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.elements['/config/link/perms'].text
942
+ permstring = config[:link][:perms].to_s
993
943
  perms = permstring.oct
994
- owner = config.elements['/config/link/owner'].text
995
- group = config.elements['/config/link/group'].text
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.xml if the user didn't specify them in
1002
- # the config.xml file.) As such link management would always fail
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.xml)
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.elements['/config/link/allow_nonexistent_dest']
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
- if config.elements['/config/pre']
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.elements['/config/link/overwrite_directory']
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.elements['/config/test_before_post'] ||
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.elements['/config/test_before_post']
1189
- if !process_test_before_post(file, config)
1190
- restore_backup(file, backup)
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
- if config.elements['/config/post']
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.elements['/config/test']
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
- if config.elements['/config/post']
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.elements['/config/test_before_post'] ||
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.elements['/config/directory'] # Directory
1166
+ if config[:directory] # Directory
1227
1167
 
1228
1168
  # A little safety check
1229
- create = config.elements['/config/directory/create']
1230
- raise "No create element found in directory section" if !create
1169
+ if !config[:directory][:create]
1170
+ raise "No create element found in directory section"
1171
+ end
1231
1172
 
1232
- permstring = config.elements['/config/directory/perms'].text
1173
+ permstring = config[:directory][:perms].to_s
1233
1174
  perms = permstring.oct
1234
- owner = config.elements['/config/directory/owner'].text
1235
- group = config.elements['/config/directory/group'].text
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
- if config.elements['/config/pre']
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.elements['/config/test_before_post'] ||
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.elements['/config/test_before_post']
1349
- if !process_test_before_post(file, config)
1350
- restore_backup(file, backup)
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
- if config.elements['/config/post']
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.elements['/config/test']
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
- if config.elements['/config/post']
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.elements['/config/test_before_post'] ||
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.elements['/config/delete'] # Delete whatever is there
1317
+ if config[:delete] # Delete whatever is there
1387
1318
 
1388
1319
  # A little safety check
1389
- proceed = config.elements['/config/delete/proceed']
1390
- raise "No proceed element found in delete section" if !proceed
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
- if config.elements['/config/pre']
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.elements['/config/delete/overwrite_directory']
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.elements['/config/test_before_post'] ||
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.elements['/config/test_before_post']
1457
- if !process_test_before_post(file, config)
1458
- restore_backup(file, backup)
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
- if config.elements['/config/post']
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.elements['/config/test']
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
- if config.elements['/config/post']
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.elements['/config/test_before_post'] ||
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, responsedata)
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 = responsedata[:allcommands][commandname]
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.elements.each('/commands/depend') do |depend|
1577
- puts "Processing command dependency #{depend.text}" if (@debug)
1578
- continue_processing = process_commands(depend.text, responsedata)
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.elements.each('/commands/dependfile') do |dependfile|
1586
- puts "Processing file dependency #{dependfile.text}" if (@debug)
1587
- continue_processing = process_file(dependfile.text, responsedata)
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.elements.each('/commands/step') do |step|
1595
- guard = step.elements['guard/exec'].text
1596
- command = step.elements['command/exec'].text
1597
-
1598
- # Run guard, display only in debug (a la setup)
1599
- guard_result = process_guard(guard, commandname)
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
- if !guard_result
1602
- # Tell the user what we're going to do
1603
- puts "Will run command '#{command}'"
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
- # If the user requested interactive mode ask them for
1606
- # confirmation to proceed.
1607
- if @interactive
1608
- case get_user_confirmation()
1609
- when CONFIRM_PROCEED
1610
- # No need to do anything
1611
- when CONFIRM_SKIP
1612
- save_results = false
1613
- throw :process_done
1614
- when CONFIRM_QUIT
1615
- continue_processing = false
1616
- save_results = false
1617
- throw :process_done
1618
- else
1619
- raise "Unexpected result from get_user_confirmation()"
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
- # Run command, always display (a la pre/post)
1624
- process_command(command, commandname)
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
- # Re-run guard, always display, abort if fails
1627
- guard_recheck_result = process_guard(guard, commandname)
1628
- if !guard_recheck_result
1629
- raise "Guard #{guard} still fails for #{commandname} after running command #{command}"
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
- contents = IO.read(file)
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
- orig_contents = IO.read(origpath)
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
- requestlist = []
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
- request = IO.read(requestfile)
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
- exectype = 'setup'
2068
- # Because the setup commands are processed every time etch runs
2069
- # (rather than just when the file has changed, as with pre/post) we
2070
- # don't want to print a message for them unless we're in debug mode.
2071
- puts "Processing #{exectype} commands" if (@debug)
2072
- config.elements.each("/config/#{exectype}/exec") do |setup|
2073
- r = process_exec(exectype, setup.text, file)
2074
- # process_exec currently raises an exception if a setup or pre command
2075
- # fails. In case that ever changes make sure we propagate
2076
- # the error.
2077
- return r if (!r)
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
- exectype = 'pre'
2082
- puts "Processing #{exectype} commands"
2083
- config.elements.each("/config/#{exectype}/exec") do |pre|
2084
- r = process_exec(exectype, pre.text, file)
2085
- # process_exec currently raises an exception if a setup or pre command
2086
- # fails. In case that ever changes make sure we propagate
2087
- # the error.
2088
- return r if (!r)
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
- # Add the "exec once" items into the list of commands to process
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.elements.each("/config/#{exectype}/exec_once") do |exec_once|
2101
- if !@exec_already_processed.has_key?(exec_once.text)
2102
- execs << exec_once.text
2103
- @exec_already_processed[exec_once] = true
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 '#{exec_once.text}', it has already " +
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 exec items as well
2112
- config.elements.each("/config/#{exectype}/exec") do |exec|
2113
- execs << exec.text
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(exectype, exec, file) }
2050
+ execs.each { |exec| process_exec(:post, exec, file) }
2120
2051
 
2121
- config.elements.each("/config/#{exectype}/exec_once_per_run") do |eopr|
2122
- # Stuff the "exec once per run" nodes into the global hash to
2123
- # be run after we've processed all files.
2124
- puts "Adding '#{eopr.text}' to 'exec once per run' list" if (@debug)
2125
- @exec_once_per_run[eopr.text] = true
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
- exectype = 'test_before_post'
2130
- puts "Processing #{exectype} commands"
2131
- config.elements.each("/config/#{exectype}/exec") do |test_before_post|
2132
- r = process_exec(exectype, test_before_post.text, file)
2133
- # If the test failed we need to propagate that error
2134
- return r if (!r)
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
- exectype = 'test'
2139
- puts "Processing #{exectype} commands"
2140
- config.elements.each("/config/#{exectype}/exec") do |test|
2141
- r = process_exec(exectype, test.text, file)
2142
- # If the test failed we need to propagate that error
2143
- return r if (!r)
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 #{exectype}" if (@debug)
2151
- process_exec(exectype, guard, commandname)
2085
+ puts "Processing guard" if (@debug)
2086
+ process_exec(:guard, guard, commandname)
2152
2087
  end
2153
2088
  def process_command(command, commandname)
2154
- exectype = 'command'
2155
- puts "Processing #{exectype}"
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 ((exectype != 'setup' && exectype != 'guard') || @debug)
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 == 'setup')
2103
+ if ! @dryrun || exectype == :guard || (@dryrun == 'damp' && exectype == :setup)
2170
2104
  etch_priority = nil
2171
2105
 
2172
- if exectype == 'post' || exectype == 'command'
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 exectype == 'post' || exectype == 'command'
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 (exectype == 'setup' || exectype == 'guard')
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 exectype == 'setup' || exectype == 'pre'
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 == 'post'
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 == 'guard'
2167
+ elsif exectype == :guard
2234
2168
  puts " Guard " + execmsg + filemsg + "exited with non-zero value"
2235
- elsif exectype == 'command'
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
- uid = nil
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.xml requests user #{user}, but that user can't be found. Using UID 0." if @debug
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
- gid = nil
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.xml requests group #{group}, but that group can't be found. Using GID 0." if @debug
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