etch 4.0.0 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
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