etch 3.19.0 → 3.20.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.
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
-