etch 3.19.0 → 3.20.0

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -3,7 +3,7 @@ spec = Gem::Specification.new do |s|
3
3
  s.name = 'etch'
4
4
  s.summary = 'Etch system configuration management client'
5
5
  s.add_dependency('facter')
6
- s.version = '3.19.0'
6
+ s.version = '3.20.0'
7
7
  s.author = 'Jason Heiss'
8
8
  s.email = 'etch-users@lists.sourceforge.net'
9
9
  s.homepage = 'http://etch.sourceforge.net'
data/bin/etch CHANGED
@@ -1,13 +1,13 @@
1
- #!/usr/bin/ruby -w
1
+ #!/usr/bin/ruby
2
2
  ##############################################################################
3
3
  # Etch configuration file management tool
4
4
  ##############################################################################
5
5
 
6
- # Ensure we can find etchclient.rb
7
- $:.unshift(File.dirname(__FILE__))
6
+ # Ensure we can find etch/client.rb when run within the development repository
7
+ $:.unshift(File.join(File.dirname(File.dirname(__FILE__)), 'lib'))
8
8
 
9
9
  require 'optparse'
10
- require 'etchclient'
10
+ require 'etch/client'
11
11
 
12
12
  #
13
13
  # Parse the command line options
@@ -69,6 +69,12 @@ end
69
69
  opts.on('--key PRIVATE_KEY', 'Use this private key for signing messages to server.') do |opt|
70
70
  options[:key] = opt
71
71
  end
72
+ opts.on('--configdir DIR', 'Directory containing etch.conf, defaults to /etc') do |opt|
73
+ options[:configdir] = opt
74
+ end
75
+ opts.on('--vardir DIR', 'Directory for etch state, defaults to /var/etch') do |opt|
76
+ options[:vardir] = opt
77
+ end
72
78
  opts.on('--test-root TESTDIR', 'For use by the test suite only.') do |opt|
73
79
  options[:file_system_root] = opt
74
80
  end
@@ -1,4 +1,4 @@
1
- #!/usr/bin/ruby -w
1
+ #!/usr/bin/ruby
2
2
 
3
3
  require 'socket'
4
4
  require 'digest/sha1'
data/bin/etch_to_trunk CHANGED
@@ -1,4 +1,4 @@
1
- #!/usr/bin/ruby -w
1
+ #!/usr/bin/ruby
2
2
 
3
3
  require 'nventory'
4
4
  require 'time'
data/lib/etch.rb CHANGED
@@ -1,11 +1,17 @@
1
- require 'find' # Find.find
2
- require 'pathname' # absolute?
3
- require 'digest/sha1' # hexdigest
4
- require 'base64' # decode64, encode64
5
- require 'fileutils' # mkdir_p
6
- require 'erb'
1
+ # Exclude standard libraries and gems from the warnings induced by
2
+ # running ruby with the -w flag. Several of these have warnings under
3
+ # ruby 1.9 and there's nothing we can do to fix that.
4
+ require 'silently'
5
+ Silently.silently do
6
+ require 'find' # Find.find
7
+ require 'pathname' # absolute?
8
+ require 'digest/sha1' # hexdigest
9
+ require 'base64' # decode64, encode64
10
+ require 'fileutils' # mkdir_p
11
+ require 'erb'
12
+ require 'logger'
13
+ end
7
14
  require 'versiontype' # Version
8
- require 'logger'
9
15
 
10
16
  class Etch
11
17
  def self.xmllib
@@ -16,27 +22,29 @@ class Etch
16
22
  end
17
23
  end
18
24
 
19
- # By default we try to use libxml, falling back to rexml if it is not
20
- # available. The xmllib environment variable can be used to force one library
21
- # or the other, mostly for testing purposes.
22
- begin
23
- if !ENV['xmllib'] || ENV['xmllib'] == 'libxml'
24
- require 'rubygems' # libxml is a gem
25
- require 'libxml'
26
- Etch.xmllib = :libxml
27
- elsif ENV['xmllib'] == 'nokogiri'
28
- require 'rubygems' # nokogiri is a gem
29
- require 'nokogiri'
30
- Etch.xmllib = :nokogiri
31
- else
32
- raise LoadError
33
- end
34
- rescue LoadError
35
- if !ENV['xmllib'] || ENV['xmllib'] == 'rexml'
36
- require 'rexml/document'
37
- Etch.xmllib = :rexml
38
- else
39
- raise
25
+ # By default we try to use nokogiri, falling back to rexml if it is not
26
+ # available. The xmllib environment variable can be used to force a specific
27
+ # library, mostly for testing purposes.
28
+ Silently.silently do
29
+ begin
30
+ if !ENV['xmllib'] || ENV['xmllib'] == 'nokogiri'
31
+ require 'rubygems' # nokogiri is a gem
32
+ require 'nokogiri'
33
+ Etch.xmllib = :nokogiri
34
+ elsif ENV['xmllib'] == 'libxml'
35
+ require 'rubygems' # libxml is a gem
36
+ require 'libxml'
37
+ Etch.xmllib = :libxml
38
+ else
39
+ raise LoadError
40
+ end
41
+ rescue LoadError
42
+ if !ENV['xmllib'] || ENV['xmllib'] == 'rexml'
43
+ require 'rexml/document'
44
+ Etch.xmllib = :rexml
45
+ else
46
+ raise
47
+ end
40
48
  end
41
49
  end
42
50
 
@@ -170,10 +178,12 @@ class Etch
170
178
  filelist = []
171
179
  if request.empty?
172
180
  @dlogger.debug "Building complete file list for request from #{@fqdn}"
173
- Find.find(@sourcebase) do |path|
174
- if File.directory?(path) && File.exist?(File.join(path, 'config.xml'))
175
- # Strip @sourcebase from start of path
176
- filelist << path.sub(Regexp.new('^' + Regexp.escape(@sourcebase)), '')
181
+ if File.exist?(@sourcebase)
182
+ Find.find(@sourcebase) do |path|
183
+ if File.directory?(path) && File.exist?(File.join(path, 'config.xml'))
184
+ # Strip @sourcebase from start of path
185
+ filelist << path.sub(Regexp.new('^' + Regexp.escape(@sourcebase)), '')
186
+ end
177
187
  end
178
188
  end
179
189
  elsif request[:files]
@@ -206,9 +216,11 @@ class Etch
206
216
  commandnames = []
207
217
  if request.empty?
208
218
  @dlogger.debug "Building complete configuration commands for request from #{@fqdn}"
209
- Find.find(@commandsbase) do |path|
210
- if File.directory?(path) && File.exist?(File.join(path, 'commands.xml'))
211
- commandnames << File.basename(path)
219
+ if File.exist?(@commandsbase)
220
+ Find.find(@commandsbase) do |path|
221
+ if File.directory?(path) && File.exist?(File.join(path, 'commands.xml'))
222
+ commandnames << File.basename(path)
223
+ end
212
224
  end
213
225
  end
214
226
  elsif request[:commands]
@@ -363,7 +375,7 @@ class Etch
363
375
 
364
376
  # Change into the corresponding directory so that the user can
365
377
  # refer to source files and scripts by their relative pathnames.
366
- Dir::chdir "#{@sourcebase}/#{file}"
378
+ Dir.chdir "#{@sourcebase}/#{file}"
367
379
 
368
380
  # See what type of action the user has requested
369
381
 
@@ -414,7 +426,7 @@ class Etch
414
426
 
415
427
  # Just slurp the file in
416
428
  plain_file = Etch.xmltext(plain_elements.first)
417
- newcontents = IO::read(plain_file)
429
+ newcontents = IO.read(plain_file)
418
430
  elsif Etch.xmlfindfirst(config_xml, '/config/file/source/template')
419
431
  template_elements = Etch.xmlarray(config_xml, '/config/file/source/template')
420
432
  if check_for_inconsistency(template_elements)
@@ -913,7 +925,7 @@ class Etch
913
925
 
914
926
  # Change into the corresponding directory so that the user can
915
927
  # refer to source files and scripts by their relative pathnames.
916
- Dir::chdir "#{@commandsbase}/#{command}"
928
+ Dir.chdir "#{@commandsbase}/#{command}"
917
929
 
918
930
  # Check that the resulting document is consistent after filtering
919
931
  remove = []
@@ -1360,7 +1372,7 @@ class Etch
1360
1372
  when :nokogiri
1361
1373
  destelem << elem.dup
1362
1374
  when :rexml
1363
- destelem.add_element(elem.clone)
1375
+ destelem.add_element(elem.deep_clone)
1364
1376
  else
1365
1377
  raise "Unknown XML library #{Etch.xmllib}"
1366
1378
  end
@@ -3,46 +3,53 @@
3
3
  ##############################################################################
4
4
 
5
5
  # Ensure we can find etch.rb if run within the development directory structure
6
- # This is roughly equivalent to "../server/lib"
7
- serverlibdir = File.join(File.dirname(File.dirname(File.expand_path(__FILE__))), 'server', 'lib')
6
+ # This is roughly equivalent to "../../../server/lib"
7
+ serverlibdir = File.join(File.dirname(File.dirname(File.dirname(File.dirname(File.expand_path(__FILE__))))), 'server', 'lib')
8
8
  if File.exist?(serverlibdir)
9
9
  $:.unshift(serverlibdir)
10
10
  end
11
11
 
12
- begin
13
- # Try loading facter w/o gems first so that we don't introduce a
14
- # dependency on gems if it is not needed.
15
- require 'facter' # Facter
16
- rescue LoadError
17
- require 'rubygems'
18
- require 'facter'
12
+ # Exclude standard libraries and gems from the warnings induced by
13
+ # running ruby with the -w flag. Several of these have warnings under
14
+ # ruby 1.9 and there's nothing we can do to fix that.
15
+ require 'silently'
16
+ Silently.silently do
17
+ begin
18
+ # Try loading facter w/o gems first so that we don't introduce a
19
+ # dependency on gems if it is not needed.
20
+ require 'facter' # Facter
21
+ rescue LoadError
22
+ require 'rubygems'
23
+ require 'facter'
24
+ end
25
+ require 'find'
26
+ require 'digest/sha1' # hexdigest
27
+ require 'openssl' # OpenSSL
28
+ require 'base64' # decode64, encode64
29
+ require 'uri'
30
+ require 'net/http'
31
+ require 'net/https'
32
+ require 'rexml/document'
33
+ require 'fileutils' # copy, mkpath, rmtree
34
+ require 'fcntl' # Fcntl::O_*
35
+ require 'etc' # getpwnam, getgrnam
36
+ require 'tempfile' # Tempfile
37
+ require 'find' # Find.find
38
+ require 'cgi'
39
+ require 'timeout'
40
+ require 'logger'
19
41
  end
20
- require 'find'
21
- require 'digest/sha1' # hexdigest
22
- require 'openssl' # OpenSSL
23
- require 'base64' # decode64, encode64
24
- require 'uri'
25
- require 'net/http'
26
- require 'net/https'
27
- require 'rexml/document'
28
- require 'fileutils' # copy, mkpath, rmtree
29
- require 'fcntl' # Fcntl::O_*
30
- require 'etc' # getpwnam, getgrnam
31
- require 'tempfile' # Tempfile
32
- require 'cgi'
33
- require 'timeout'
34
- require 'logger'
35
42
  require 'etch'
36
43
 
37
44
  class Etch::Client
38
- VERSION = '3.19.0'
45
+ VERSION = '3.20.0'
39
46
 
40
47
  CONFIRM_PROCEED = 1
41
48
  CONFIRM_SKIP = 2
42
49
  CONFIRM_QUIT = 3
43
50
  PRIVATE_KEY_PATHS = ["/etc/ssh/ssh_host_rsa_key", "/etc/ssh_host_rsa_key"]
44
51
  DEFAULT_CONFIGDIR = '/etc'
45
- DEFAULT_VARBASE = '/var/etch'
52
+ DEFAULT_VARDIR = '/var/etch'
46
53
  DEFAULT_DETAILED_RESULTS = ['SERVER']
47
54
 
48
55
  # We need these in relation to the output capturing
@@ -53,6 +60,8 @@ class Etch::Client
53
60
 
54
61
  def initialize(options)
55
62
  @server = options[:server] ? options[:server] : 'https://etch'
63
+ @configdir = options[:configdir] ? options[:configdir] : DEFAULT_CONFIGDIR
64
+ @vardir = options[:vardir] ? options[:vardir] : DEFAULT_VARDIR
56
65
  @tag = options[:tag]
57
66
  @local = options[:local] ? File.expand_path(options[:local]) : nil
58
67
  @debug = options[:debug]
@@ -64,17 +73,14 @@ class Etch::Client
64
73
  @key = options[:key] ? options[:key] : get_private_key_path
65
74
  @disableforce = options[:disableforce]
66
75
  @lockforce = options[:lockforce]
67
-
68
- @last_response = ""
69
76
 
70
- @configdir = DEFAULT_CONFIGDIR
71
- @varbase = DEFAULT_VARBASE
77
+ @last_response = ""
72
78
 
73
79
  @file_system_root = '/' # Not sure if this needs to be more portable
74
80
  # This option is only intended for use by the test suite
75
81
  if options[:file_system_root]
76
82
  @file_system_root = options[:file_system_root]
77
- @varbase = File.join(@file_system_root, @varbase)
83
+ @vardir = File.join(@file_system_root, @vardir)
78
84
  @configdir = File.join(@file_system_root, @configdir)
79
85
  end
80
86
 
@@ -142,10 +148,10 @@ class Etch::Client
142
148
  @detailed_results = DEFAULT_DETAILED_RESULTS
143
149
  end
144
150
 
145
- @origbase = File.join(@varbase, 'orig')
146
- @historybase = File.join(@varbase, 'history')
147
- @lockbase = File.join(@varbase, 'locks')
148
- @requestbase = File.join(@varbase, 'requests')
151
+ @origbase = File.join(@vardir, 'orig')
152
+ @historybase = File.join(@vardir, 'history')
153
+ @lockbase = File.join(@vardir, 'locks')
154
+ @requestbase = File.join(@vardir, 'requests')
149
155
 
150
156
  @facts = Facter.to_hash
151
157
  if @facts['operatingsystemrelease']
@@ -163,6 +169,10 @@ class Etch::Client
163
169
  else
164
170
  dlogger.level = Logger::INFO
165
171
  end
172
+ blankrequest = {}
173
+ @facts.each_pair { |key, value| blankrequest[key] = value.to_s }
174
+ blankrequest['fqdn'] = @facts['fqdn']
175
+ @facts = blankrequest
166
176
  @etch = Etch.new(logger, dlogger)
167
177
  else
168
178
  # Make sure the server URL ends in a / so that we can append paths
@@ -215,6 +225,7 @@ class Etch::Client
215
225
  # Prep http instance
216
226
  http = nil
217
227
  if !@local
228
+ puts "Connecting to #{@filesuri}" if (@debug)
218
229
  http = Net::HTTP.new(@filesuri.host, @filesuri.port)
219
230
  if @filesuri.scheme == "https"
220
231
  # Eliminate the OpenSSL "using default DH parameters" warning
@@ -565,7 +576,7 @@ class Etch::Client
565
576
  end
566
577
 
567
578
  def check_for_disable_etch_file
568
- disable_etch = File.join(@varbase, 'disable_etch')
579
+ disable_etch = File.join(@vardir, 'disable_etch')
569
580
  message = ''
570
581
  if File.exist?(disable_etch)
571
582
  if !@disableforce
@@ -1803,7 +1814,7 @@ class Etch::Client
1803
1814
  puts "Original file #{file} doesn't exist, saving that state permanently as #{origpath}"
1804
1815
  end
1805
1816
  if proceed
1806
- File.open(origpath, 'w') { |file| } if (!@dryrun)
1817
+ File.open(origpath, 'w') { |origfile| } if (!@dryrun)
1807
1818
  end
1808
1819
  end
1809
1820
 
@@ -2014,7 +2025,7 @@ class Etch::Client
2014
2025
  else
2015
2026
  # If there's no file to back up then leave a marker file so
2016
2027
  # that restore_backup does the right thing
2017
- File.open("#{backuppath}.NOORIG", "w") { |file| }
2028
+ File.open("#{backuppath}.NOORIG", "w") { |markerfile| }
2018
2029
  end
2019
2030
  end
2020
2031
 
@@ -2375,7 +2386,7 @@ class Etch::Client
2375
2386
  begin
2376
2387
  fd = IO::sysopen(lockpath, Fcntl::O_WRONLY|Fcntl::O_CREAT|Fcntl::O_EXCL)
2377
2388
  puts "Lock acquired for #{file}" if (@debug)
2378
- f = IO.open(fd) { |f| f.puts $$ }
2389
+ f = IO.open(fd) { |lockfile| lockfile.puts $$ }
2379
2390
  @locked_files[file] = true
2380
2391
  return
2381
2392
  rescue Errno::EEXIST
@@ -2420,13 +2431,15 @@ class Etch::Client
2420
2431
  # and can be removed. If told to force we remove all lockfiles.
2421
2432
  def remove_stale_lock_files
2422
2433
  twohoursago = Time.at(Time.now - 60 * 60 * 2)
2423
- Find.find(@lockbase) do |file|
2424
- next unless file =~ /\.LOCK$/
2425
- next unless File.file?(file)
2426
-
2427
- if @lockforce || File.mtime(file) < twohoursago
2428
- puts "Removing stale lock file #{file}"
2429
- File.delete(file)
2434
+ if File.exist?(@lockbase)
2435
+ Find.find(@lockbase) do |file|
2436
+ next unless file =~ /\.LOCK$/
2437
+ next unless File.file?(file)
2438
+
2439
+ if @lockforce || File.mtime(file) < twohoursago
2440
+ puts "Removing stale lock file #{file}"
2441
+ File.delete(file)
2442
+ end
2430
2443
  end
2431
2444
  end
2432
2445
  end
@@ -2439,6 +2452,8 @@ class Etch::Client
2439
2452
  # for etch to handle any given file, including running any
2440
2453
  # setup/pre/post commands.
2441
2454
  OUTPUT_CAPTURE_TIMEOUT = 5 * 60
2455
+ # In interactive mode bump the timeout up to something absurdly large
2456
+ OUTPUT_CAPTURE_INTERACTIVE_TIMEOUT = 14 * 24 * 60 * 60
2442
2457
  def start_output_capture
2443
2458
  # Establish a pipe, spawn a child process, and redirect stdout/stderr
2444
2459
  # to the pipe. The child gathers up anything sent over the pipe and
@@ -2490,7 +2505,13 @@ class Etch::Client
2490
2505
  # capturing feature this results in etch hanging around forever
2491
2506
  # waiting for the pipes to close. We time out after a suitable
2492
2507
  # period of time so that etch processes don't hang around forever.
2493
- Timeout.timeout(OUTPUT_CAPTURE_TIMEOUT) do
2508
+ timeout = nil
2509
+ if @interactive
2510
+ timeout = OUTPUT_CAPTURE_INTERACTIVE_TIMEOUT
2511
+ else
2512
+ timeout = OUTPUT_CAPTURE_TIMEOUT
2513
+ end
2514
+ Timeout.timeout(timeout) do
2494
2515
  while char = pread.getc
2495
2516
  putc(char)
2496
2517
  output << char.chr
data/lib/silently.rb ADDED
@@ -0,0 +1,11 @@
1
+ # http://www.caliban.org/ruby/rubyguide.shtml#warnings
2
+ class Silently
3
+ def self.silently(&block)
4
+ warn_level = $VERBOSE
5
+ $VERBOSE = nil
6
+ result = block.call
7
+ $VERBOSE = warn_level
8
+ result
9
+ end
10
+ end
11
+
metadata CHANGED
@@ -1,72 +1,66 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: etch
3
- version: !ruby/object:Gem::Version
4
- version: 3.19.0
3
+ version: !ruby/object:Gem::Version
4
+ version: 3.20.0
5
+ prerelease:
5
6
  platform: ruby
6
- authors:
7
+ authors:
7
8
  - Jason Heiss
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
-
12
- date: 2011-04-12 00:00:00 -07:00
13
- default_executable:
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
12
+ date: 2012-03-19 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
16
15
  name: facter
16
+ requirement: &70362060167060 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
17
22
  type: :runtime
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
20
- requirements:
21
- - - ">="
22
- - !ruby/object:Gem::Version
23
- version: "0"
24
- version:
23
+ prerelease: false
24
+ version_requirements: *70362060167060
25
25
  description:
26
26
  email: etch-users@lists.sourceforge.net
27
- executables:
27
+ executables:
28
28
  - etch
29
29
  - etch_to_trunk
30
30
  - etch_cron_wrapper
31
31
  extensions: []
32
-
33
32
  extra_rdoc_files: []
34
-
35
- files:
33
+ files:
36
34
  - bin/etch
37
35
  - bin/etch_cron_wrapper
38
36
  - bin/etch_to_trunk
37
+ - lib/etch/client.rb
39
38
  - lib/etch.rb
40
- - lib/etchclient.rb
39
+ - lib/silently.rb
41
40
  - lib/versiontype.rb
42
41
  - Rakefile
43
- has_rdoc: true
44
42
  homepage: http://etch.sourceforge.net
45
43
  licenses: []
46
-
47
44
  post_install_message:
48
45
  rdoc_options: []
49
-
50
- require_paths:
46
+ require_paths:
51
47
  - lib
52
- required_ruby_version: !ruby/object:Gem::Requirement
53
- requirements:
54
- - - ">="
55
- - !ruby/object:Gem::Version
56
- version: "1.8"
57
- version:
58
- required_rubygems_version: !ruby/object:Gem::Requirement
59
- requirements:
60
- - - ">="
61
- - !ruby/object:Gem::Version
62
- version: "0"
63
- version:
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '1.8'
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ! '>='
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
64
60
  requirements: []
65
-
66
61
  rubyforge_project: etchsyscm
67
- rubygems_version: 1.3.5
62
+ rubygems_version: 1.8.15
68
63
  signing_key:
69
64
  specification_version: 3
70
65
  summary: Etch system configuration management client
71
66
  test_files: []
72
-