rouster 0.53 → 0.57

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.
@@ -12,13 +12,12 @@ class Rouster
12
12
  # runs `crontab -l <user>` and parses output, returns hash:
13
13
  # {
14
14
  # user => {
15
- # logicalOrderInt => {
15
+ # command => {
16
16
  # :minute => minute,
17
17
  # :hour => hour,
18
18
  # :dom => dom, # day of month
19
19
  # :mon => mon, # month
20
20
  # :dow => dow, # day of week
21
- # :command => command,
22
21
  # }
23
22
  # }
24
23
  # }
@@ -33,15 +32,15 @@ class Rouster
33
32
  if cache and self.deltas[:crontab].class.eql?(Hash)
34
33
 
35
34
  if self.cache_timeout and self.cache_timeout.is_a?(Integer) and (Time.now.to_i - self.cache[:crontab]) > self.cache_timeout
36
- @log.debug(sprintf('invalidating [crontab] cache, was [%s] old, allowed [%s]', (Time.now.to_i - self.cache[:crontab]), self.cache_timeout))
35
+ @logger.debug(sprintf('invalidating [crontab] cache, was [%s] old, allowed [%s]', (Time.now.to_i - self.cache[:crontab]), self.cache_timeout))
37
36
  self.deltas.delete(:crontab)
38
37
  end
39
38
 
40
39
  if self.deltas.has_key?(:crontab) and self.deltas[:crontab].has_key?(user)
41
- @log.debug(sprintf('using cached [crontab] from [%s]', self.cache[:crontab]))
40
+ @logger.debug(sprintf('using cached [crontab] from [%s]', self.cache[:crontab]))
42
41
  return self.deltas[:crontab][user]
43
42
  elsif self.deltas.has_key?(:crontab) and user.eql?('*')
44
- @log.debug(sprintf('using cached [crontab] from [%s]', self.cache[:crontab]))
43
+ @logger.debug(sprintf('using cached [crontab] from [%s]', self.cache[:crontab]))
45
44
  return self.deltas[:crontab]
46
45
  else
47
46
  # noop fallthrough to gather data to cache
@@ -49,7 +48,6 @@ class Rouster
49
48
 
50
49
  end
51
50
 
52
- i = 0
53
51
  res = Hash.new
54
52
  users = nil
55
53
 
@@ -69,25 +67,30 @@ class Rouster
69
67
  end
70
68
 
71
69
  raw.split("\n").each do |line|
72
- next if line.match(/^#/)
70
+ next if line.match(/^#|^\s+$/)
73
71
  elements = line.split("\s")
74
72
 
73
+ command = elements[5..elements.size].join(' ')
74
+
75
75
  res[u] ||= Hash.new
76
- res[u][i] ||= Hash.new
77
-
78
- res[u][i][:minute] = elements[0]
79
- res[u][i][:hour] = elements[1]
80
- res[u][i][:dom] = elements[2]
81
- res[u][i][:mon] = elements[3]
82
- res[u][i][:dow] = elements[4]
83
- res[u][i][:command] = elements[5..elements.size].join(' ')
84
- end
85
76
 
86
- i += 1
77
+ if res[u][command].class.eql?(Hash)
78
+ unique = elements.join('')
79
+ command = sprintf('%s-duplicate.%s', command, unique)
80
+ @logger.info(sprintf('duplicate crontab command found, adding hash key[%s]', command))
81
+ end
82
+
83
+ res[u][command] = Hash.new
84
+ res[u][command][:minute] = elements[0]
85
+ res[u][command][:hour] = elements[1]
86
+ res[u][command][:dom] = elements[2]
87
+ res[u][command][:mon] = elements[3]
88
+ res[u][command][:dow] = elements[4]
89
+ end
87
90
  end
88
91
 
89
92
  if cache
90
- @log.debug(sprintf('caching [crontab] at [%s]', Time.now.asctime))
93
+ @logger.debug(sprintf('caching [crontab] at [%s]', Time.now.asctime))
91
94
 
92
95
  if ! user.eql?('*')
93
96
  self.deltas[:crontab] ||= Hash.new
@@ -124,10 +127,10 @@ class Rouster
124
127
  if cache and ! self.deltas[:groups].nil?
125
128
 
126
129
  if self.cache_timeout and self.cache_timeout.is_a?(Integer) and (Time.now.to_i - self.cache[:groups]) > self.cache_timeout
127
- @log.debug(sprintf('invalidating [groups] cache, was [%s] old, allowed [%s]', (Time.now.to_i - self.cache[:groups]), self.cache_timeout))
130
+ @logger.debug(sprintf('invalidating [groups] cache, was [%s] old, allowed [%s]', (Time.now.to_i - self.cache[:groups]), self.cache_timeout))
128
131
  self.deltas.delete(:groups)
129
132
  else
130
- @log.debug(sprintf('using cached [groups] from [%s]', self.cache[:groups]))
133
+ @logger.debug(sprintf('using cached [groups] from [%s]', self.cache[:groups]))
131
134
  return self.deltas[:groups]
132
135
  end
133
136
 
@@ -166,7 +169,7 @@ class Rouster
166
169
  gid = users[user][:gid]
167
170
 
168
171
  unless known_valid_gids.member?(gid)
169
- @log.warn(sprintf('found user[%s] with unknown GID[%s], known GIDs[%s]', user, gid, known_valid_gids))
172
+ @logger.warn(sprintf('found user[%s] with unknown GID[%s], known GIDs[%s]', user, gid, known_valid_gids))
170
173
  next
171
174
  end
172
175
 
@@ -186,7 +189,7 @@ class Rouster
186
189
  end
187
190
 
188
191
  if cache
189
- @log.debug(sprintf('caching [groups] at [%s]', Time.now.asctime))
192
+ @logger.debug(sprintf('caching [groups] at [%s]', Time.now.asctime))
190
193
  self.deltas[:groups] = groups
191
194
  self.cache[:groups] = Time.now.to_i
192
195
  end
@@ -219,10 +222,10 @@ class Rouster
219
222
  if cache and ! self.deltas[:packages].nil?
220
223
 
221
224
  if self.cache_timeout and self.cache_timeout.is_a?(Integer) and (Time.now.to_i - self.cache[:packages]) > self.cache_timeout
222
- @log.debug(sprintf('invalidating [packages] cache, was [%s] old, allowed [%s]', (Time.now.to_i - self.cache[:packages]), self.cache_timeout))
225
+ @logger.debug(sprintf('invalidating [packages] cache, was [%s] old, allowed [%s]', (Time.now.to_i - self.cache[:packages]), self.cache_timeout))
223
226
  self.deltas.delete(:packages)
224
227
  else
225
- @log.debug(sprintf('using cached [packages] from [%s]', self.cache[:packages]))
228
+ @logger.debug(sprintf('using cached [packages] from [%s]', self.cache[:packages]))
226
229
  return self.deltas[:packages]
227
230
  end
228
231
 
@@ -299,7 +302,7 @@ class Rouster
299
302
  end
300
303
 
301
304
  if cache
302
- @log.debug(sprintf('caching [packages] at [%s]', Time.now.asctime))
305
+ @logger.debug(sprintf('caching [packages] at [%s]', Time.now.asctime))
303
306
  self.deltas[:packages] = res
304
307
  self.cache[:packages] = Time.now.to_i
305
308
  end
@@ -332,10 +335,10 @@ class Rouster
332
335
 
333
336
  if cache and ! self.deltas[:ports].nil?
334
337
  if self.cache_timeout and self.cache_timeout.is_a?(Integer) and (Time.now.to_i - self.cache[:ports]) > self.cache_timeout
335
- @log.debug(sprintf('invalidating [ports] cache, was [%s] old, allowed [%s]', (Time.now.to_i - self.cache[:ports]), self.cache_timeout))
338
+ @logger.debug(sprintf('invalidating [ports] cache, was [%s] old, allowed [%s]', (Time.now.to_i - self.cache[:ports]), self.cache_timeout))
336
339
  self.deltas.delete(:ports)
337
340
  else
338
- @log.debug(sprintf('using cached [ports] from [%s]', self.cache[:ports]))
341
+ @logger.debug(sprintf('using cached [ports] from [%s]', self.cache[:ports]))
339
342
  return self.deltas[:ports]
340
343
  end
341
344
  end
@@ -367,7 +370,7 @@ class Rouster
367
370
  end
368
371
 
369
372
  if cache
370
- @log.debug(sprintf('caching [ports] at [%s]', Time.now.asctime))
373
+ @logger.debug(sprintf('caching [ports] at [%s]', Time.now.asctime))
371
374
  self.deltas[:ports] = res
372
375
  self.cache[:ports] = Time.now.to_i
373
376
  end
@@ -400,10 +403,10 @@ class Rouster
400
403
  if cache and ! self.deltas[:services].nil?
401
404
 
402
405
  if self.cache_timeout and self.cache_timeout.is_a?(Integer) and (Time.now.to_i - self.cache[:services]) > self.cache_timeout
403
- @log.debug(sprintf('invalidating [services] cache, was [%s] old, allowed [%s]', (Time.now.to_i - self.cache[:services]), self.cache_timeout))
406
+ @logger.debug(sprintf('invalidating [services] cache, was [%s] old, allowed [%s]', (Time.now.to_i - self.cache[:services]), self.cache_timeout))
404
407
  self.deltas.delete(:services)
405
408
  else
406
- @log.debug(sprintf('using cached [services] from [%s]', self.cache[:services]))
409
+ @logger.debug(sprintf('using cached [services] from [%s]', self.cache[:services]))
407
410
  return self.deltas[:services]
408
411
  end
409
412
 
@@ -492,18 +495,20 @@ class Rouster
492
495
 
493
496
  if line.match(/^(\w+?)\sis\s(.*)$/)
494
497
  # <service> is <state>
495
- res[$1] = $2
498
+ name = $1
499
+ state = $2
500
+ res[name] = state
496
501
 
497
- if $2.match(/^not/)
502
+ if state.match(/^not/)
498
503
  # this catches 'Kdump is not operational'
499
- res[$1] = 'stopped'
504
+ res[name] = 'stopped'
500
505
  end
501
506
 
502
507
  elsif line.match(/^(\w+?)\s\(pid.*?\)\sis\s(\w+)$/)
503
508
  # <service> (pid <pid> [pid]) is <state>...
504
509
  res[$1] = $2
505
510
  elsif line.match(/^(\w+?)\sis\s(\w+)\.*$/) # not sure this is actually needed
506
- @log.debug('triggered supposedly unnecessary regex')
511
+ @logger.debug('triggered supposedly unnecessary regex')
507
512
  # <service> is <state>. whatever
508
513
  res[$1] = $2
509
514
  elsif line.match(/^(\w+?)\:.*?(\w+)$/)
@@ -529,7 +534,7 @@ class Rouster
529
534
  if humanize
530
535
  res.each_pair do |k,v|
531
536
  next if allowed_modes.member?(v)
532
- @log.debug(sprintf('replacing service[%s] status of [%s] with [%s] for uniformity', k, v, failover_mode))
537
+ @logger.debug(sprintf('replacing service[%s] status of [%s] with [%s] for uniformity', k, v, failover_mode))
533
538
  res[k] = failover_mode
534
539
  end
535
540
  end
@@ -539,7 +544,7 @@ class Rouster
539
544
  end
540
545
 
541
546
  if cache
542
- @log.debug(sprintf('caching [services] at [%s]', Time.now.asctime))
547
+ @logger.debug(sprintf('caching [services] at [%s]', Time.now.asctime))
543
548
  self.deltas[:services] = res
544
549
  self.cache[:services] = Time.now.to_i
545
550
  end
@@ -566,10 +571,10 @@ class Rouster
566
571
  if cache and ! self.deltas[:users].nil?
567
572
 
568
573
  if self.cache_timeout and self.cache_timeout.is_a?(Integer) and (Time.now.to_i - self.cache[:users]) > self.cache_timeout
569
- @log.debug(sprintf('invalidating [users] cache, was [%s] old, allowed [%s]', (Time.now.to_i - self.cache[:users]), self.cache_timeout))
574
+ @logger.debug(sprintf('invalidating [users] cache, was [%s] old, allowed [%s]', (Time.now.to_i - self.cache[:users]), self.cache_timeout))
570
575
  self.deltas.delete(:users)
571
576
  else
572
- @log.debug(sprintf('using cached [users] from [%s]', self.cache[:users]))
577
+ @logger.debug(sprintf('using cached [users] from [%s]', self.cache[:users]))
573
578
  return self.deltas[:users]
574
579
  end
575
580
 
@@ -594,7 +599,7 @@ class Rouster
594
599
  end
595
600
 
596
601
  if cache
597
- @log.debug(sprintf('caching [users] at [%s]', Time.now.asctime))
602
+ @logger.debug(sprintf('caching [users] at [%s]', Time.now.asctime))
598
603
  self.deltas[:users] = res
599
604
  self.cache[:users] = Time.now.to_i
600
605
  end
@@ -20,10 +20,10 @@ class Rouster
20
20
  if cache.true? and ! self.facts.nil?
21
21
 
22
22
  if self.cache_timeout and self.cache_timeout.is_a?(Integer) and (Time.now.to_i - self.cache[:facter]) > self.cache_timeout
23
- @log.debug(sprintf('invalidating [facter] cache, was [%s] old, allowed [%s]', (Time.now.to_i - self.cache[:facter]), self.cache_timeout))
23
+ @logger.debug(sprintf('invalidating [facter] cache, was [%s] old, allowed [%s]', (Time.now.to_i - self.cache[:facter]), self.cache_timeout))
24
24
  self.facts = nil
25
25
  else
26
- @log.debug(sprintf('using cached [facter] from [%s]', self.cache[:facter]))
26
+ @logger.debug(sprintf('using cached [facter] from [%s]', self.cache[:facter]))
27
27
  return self.facts
28
28
  end
29
29
 
@@ -38,7 +38,7 @@ class Rouster
38
38
  end
39
39
 
40
40
  if cache.true?
41
- @log.debug(sprintf('caching [facter] at [%s]', Time.now.asctime))
41
+ @logger.debug(sprintf('caching [facter] at [%s]', Time.now.asctime))
42
42
  self.facts = res
43
43
  self.cache[:facter] = Time.now.to_i
44
44
  end
@@ -93,8 +93,17 @@ class Rouster
93
93
  # parameters
94
94
  # * [input] - string to look at, defaults to self.get_output()
95
95
  def get_puppet_errors(input=nil)
96
- str = input.nil? ? self.get_output() : input
97
- errors = str.scan(/35merr:.*/)
96
+ str = input.nil? ? self.get_output() : input
97
+ errors = nil
98
+ errors_27 = str.scan(/35merr:.*/)
99
+ errors_30 = str.scan(/Error:.*/)
100
+
101
+ # TODO this is a little less than efficient, don't scan for 3.0 if you found 2.7
102
+ if errors_27.size > 0
103
+ errors = errors_27
104
+ else
105
+ errors = errors_30
106
+ end
98
107
 
99
108
  errors.empty? ? nil : errors
100
109
  end
@@ -107,8 +116,17 @@ class Rouster
107
116
  # parameters
108
117
  # * [input] - string to look at, defaults to self.get_output()
109
118
  def get_puppet_notices(input=nil)
110
- str = input.nil? ? self.get_output() : input
111
- notices = str.scan(/36mnotice:.*/)
119
+ str = input.nil? ? self.get_output() : input
120
+ notices = nil
121
+ notices_27 = str.scan(/36mnotice:.*/) # not sure when this stopped working
122
+ notices_30 = str.scan(/Notice:.*/)
123
+
124
+ # TODO this is a little less than efficient, don't scan for 3.0 if you found 2.7
125
+ if notices_27.size > 0
126
+ notices = notices_27
127
+ else
128
+ notices = notices_30
129
+ end
112
130
 
113
131
  notices.empty? ? nil : notices
114
132
  end
@@ -151,7 +169,7 @@ class Rouster
151
169
  cmd = 'hiera'
152
170
 
153
171
  if facts.nil?
154
- @log.info('no facts provided, calling facter() automatically')
172
+ @logger.info('no facts provided, calling facter() automatically')
155
173
  facts = self.facter()
156
174
  end
157
175
 
@@ -279,14 +297,13 @@ class Rouster
279
297
  end
280
298
  end
281
299
 
282
-
283
300
  results[:classes] = classes
284
301
  results[:resources] = resources
285
302
 
286
303
  results
287
304
  end
288
305
 
289
- # TODO: come up with better method names here.. remove_existing_certs() and remove_my_cert() are not very descriptive
306
+ # TODO: come up with better method names here.. remove_existing_certs() and remove_specific_cert() are not very descriptive
290
307
 
291
308
  ##
292
309
  # remove_existing_certs
@@ -307,16 +324,46 @@ class Rouster
307
324
  hacky_break = false
308
325
 
309
326
  except.each do |exception|
327
+ next if hacky_break
310
328
  hacky_break = line.match(/#{exception}/)
329
+ end
311
330
 
312
- next if hacky_break
331
+ next if hacky_break
332
+
333
+ host = $1 if line.match(/^\+\s"(.*?)"/)
334
+
335
+ hosts.push(host) unless host.nil? # only want to clear signed certs
336
+ end
337
+
338
+ hosts.each do |host|
339
+ self.run(sprintf('puppet cert --clean %s', host))
340
+ end
341
+
342
+ end
343
+
344
+ ##
345
+ # remove_specific_cert
346
+ #
347
+ # ... removes a specific (or several specific) certificates, effectively the reverse of remove_existing_certs() - and again, really only useful when called on a puppet master
348
+ def remove_specific_cert (targets)
349
+ targets = targets.kind_of?(Array) ? targets : [targets]
350
+ hosts = Array.new()
351
+
352
+ res = self.run('puppet cert list --all')
353
+
354
+ res.each_line do |line|
355
+ hacky_break = true
356
+
357
+ targets.each do |target|
358
+ next unless hacky_break
359
+ hacky_break = line.match(/#{target}/)
313
360
  end
314
361
 
315
362
  next unless hacky_break
316
363
 
317
364
  host = $1 if line.match(/^\+\s"(.*?)"/)
365
+ hosts.push(host) unless host.nil?
318
366
 
319
- hosts.push(host) unless host.nil? # only want to clear signed certs
320
367
  end
321
368
 
322
369
  hosts.each do |host|
@@ -325,6 +372,7 @@ class Rouster
325
372
 
326
373
  end
327
374
 
375
+
328
376
  ##
329
377
  # run_puppet
330
378
  #
@@ -355,7 +403,7 @@ class Rouster
355
403
  # parameters
356
404
  # * [mode] - method to run puppet, defaults to 'master'
357
405
  # * [opts] - hash of additional options
358
- def run_puppet(mode='master', passed_opts=nil)
406
+ def run_puppet(mode='master', passed_opts={})
359
407
 
360
408
  if mode.eql?('master')
361
409
  opts = {
@@ -391,22 +439,33 @@ class Rouster
391
439
  :additional_options => nil
392
440
  }.merge!(passed_opts)
393
441
 
394
- ## validate required arguments
395
- raise InternalError.new(sprintf('invalid hiera config specified[%s]', opts[:hiera_config])) unless self.is_file?(opts[:hiera_config])
396
- raise InternalError.new(sprintf('invalid module dir specified[%s]', opts[:module_dir])) unless self.is_dir?(opts[:module_dir])
442
+ ## validate arguments -- can do better here (:manifest_dir, :manifest_file)
443
+ puppet_version = self.get_puppet_version() # hiera_config specification is only supported in >3.0, but NOT required anywhere
444
+
445
+ if opts[:hiera_config]
446
+ if puppet_version > '3.0'
447
+ raise InternalError.new(sprintf('invalid hiera config specified[%s]', opts[:hiera_config])) unless self.is_file?(opts[:hiera_config])
448
+ else
449
+ @logger.error(sprintf('puppet version[%s] does not support --hiera_config, ignoring', puppet_version))
450
+ end
451
+ end
397
452
 
398
- puppet_version = self.get_puppet_version() # hiera_config specification is only supported in >3.0
453
+ if opts[:module_dir]
454
+ raise InternalError.new(sprintf('invalid module dir specified[%s]', opts[:module_dir])) unless self.is_dir?(opts[:module_dir])
455
+ end
399
456
 
400
457
  if opts[:manifest_file]
401
458
  opts[:manifest_file] = opts[:manifest_file].class.eql?(Array) ? opts[:manifest_file] : [opts[:manifest_file]]
402
459
  opts[:manifest_file].each do |file|
403
460
  raise InternalError.new(sprintf('invalid manifest file specified[%s]', file)) unless self.is_file?(file)
404
461
 
405
- cmd = sprintf('puppet apply %s --modulepath=%s', (puppet_version > '3.0') ? "--hiera_config=#{opts[:hiera_config]}" : '', opts[:module_dir])
462
+ cmd = 'puppet apply'
463
+ cmd << sprintf(' --modulepath=%s', opts[:module_dir]) unless opts[:module_dir].nil?
464
+ cmd << sprintf(' --hiera_config=%s', opts[:hiera_config]) unless opts[:hiera_config].nil? or puppet_version < '3.0'
406
465
  cmd << sprintf(' --environment %s', opts[:environment]) unless opts[:environment].nil?
407
466
  cmd << sprintf(' --certname %s', opts[:certname]) unless opts[:certname].nil?
408
467
  cmd << ' --pluginsync' if opts[:pluginsync]
409
- cmd << opts[:additional_options] unless opts[:additional_options].nil?
468
+ cmd << sprintf(' %s', opts[:additional_options]) unless opts[:additional_options].nil?
410
469
  cmd << sprintf(' %s', file)
411
470
 
412
471
  self.run(cmd, opts[:expected_exitcode])
@@ -422,11 +481,13 @@ class Rouster
422
481
 
423
482
  manifests.each do |m|
424
483
 
425
- cmd = sprintf('puppet apply %s --modulepath=%s', (puppet_version > '3.0') ? "--hiera_config=#{opts[:hiera_config]}" : '', opts[:module_dir])
484
+ cmd = 'puppet apply'
485
+ cmd << sprintf(' --modulepath=%s', opts[:module_dir]) unless opts[:module_dir].nil?
486
+ cmd << sprintf(' --hiera_config=%s', opts[:hiera_config]) unless opts[:hiera_config].nil? or puppet_version < '3.0'
426
487
  cmd << sprintf(' --environment %s', opts[:environment]) unless opts[:environment].nil?
427
488
  cmd << sprintf(' --certname %s', opts[:certname]) unless opts[:certname].nil?
428
489
  cmd << ' --pluginsync' if opts[:pluginsync]
429
- cmd << opts[:additional_options] unless opts[:additional_options].nil?
490
+ cmd << sprintf(' %s', opts[:additional_options]) unless opts[:additional_options].nil?
430
491
  cmd << sprintf(' %s', m)
431
492
 
432
493
  self.run(cmd, opts[:expected_exitcode])
@@ -8,7 +8,99 @@ class Rouster
8
8
  ##
9
9
  # validate_cron
10
10
  #
11
- # TODO how do we actually want to implement this? since cron jobs don't have names, what does the user specify instead?
11
+ # given the name of the user who owns crontab, the cron's command to execute and a hash of expectations, returns true|false whether cron matches expectations
12
+ #
13
+ # parameters
14
+ # * <user> - name of user who owns crontab
15
+ # * <name> - the cron's command to execute
16
+ # * <expectations> - hash of expectations, see examples
17
+ # * <fail_fast> - return false immediately on any failure (default is false)
18
+ #
19
+ # example expectations:
20
+ # 'username',
21
+ # '/home/username/test.pl', {
22
+ # :ensure => 'present',
23
+ # :minute => 1,
24
+ # :hour => 0,
25
+ # :dom => '*',
26
+ # :mon => '*',
27
+ # :dow => '*',
28
+ # }
29
+ #
30
+ # 'root',
31
+ # 'printf > /var/log/apache/error_log', {
32
+ # :minute => 59,
33
+ # :hour => [8, 12],
34
+ # :dom => '*',
35
+ # :mon => '*',
36
+ # :dow => '*',
37
+ # }
38
+ #
39
+ # supported keys:
40
+ # * :exists|:ensure -- defaults to present if not specified
41
+ # * :minute
42
+ # * :hour
43
+ # * :dom -- day of month
44
+ # * :mon -- month
45
+ # * :dow -- day of week
46
+ # * :constrain
47
+ def validate_cron(user, name, expectations, fail_fast=false)
48
+ if user.nil?
49
+ raise InternalError.new('no user specified constraint')
50
+ end
51
+
52
+ crontabs = self.get_crontab(user)
53
+
54
+ if expectations[:ensure].nil? and expectations[:exists].nil?
55
+ expectations[:ensure] = 'present'
56
+ end
57
+
58
+ if expectations.has_key?(:constrain)
59
+ expectations[:constrain] = expectations[:constrain].class.eql?(Array) ? expectations[:constrain] : [expectations[:constrain]]
60
+
61
+ expectations[:constrain].each do |constraint|
62
+ fact, expectation = constraint.split("\s")
63
+ unless meets_constraint?(fact, expectation)
64
+ @logger.info(sprintf('returning true for expectation [%s], did not meet constraint[%s/%s]', name, fact, expectation))
65
+ return true
66
+ end
67
+ end
68
+
69
+ expectations.delete(:constrain)
70
+ end
71
+
72
+ results = Hash.new()
73
+ local = nil
74
+
75
+ expectations.each do |k,v|
76
+ case k
77
+ when :ensure, :exists
78
+ if crontabs.has_key?(name)
79
+ if v.to_s.match(/absent|false/).nil?
80
+ local = true
81
+ else
82
+ local = false
83
+ end
84
+ else
85
+ local = v.to_s.match(/absent|false/).nil? ? false : true
86
+ end
87
+ when :minute, :hour, :dom, :mon, :dow
88
+ if crontabs.has_key?(name) and crontabs[name].has_key?(k) and crontabs[name][k].to_s.eql?(v.to_s)
89
+ local = true
90
+ else
91
+ local = false
92
+ end
93
+ else
94
+ raise InternalError.new(sprintf('unknown expectation[%s / %s]', k, v))
95
+ end
96
+
97
+ return false if local.eql?(false) and fail_fast.eql?(true)
98
+ results[k] = local
99
+ end
100
+
101
+ @logger.info("#{name} [#{expectations}] => #{results}")
102
+ results.find{|k,v| v.false? }.nil?
103
+ end
12
104
 
13
105
  ##
14
106
  # validate_file
@@ -72,7 +164,7 @@ class Rouster
72
164
  expectation = $2
73
165
 
74
166
  unless meets_constraint?(fact, expectation)
75
- @log.info(sprintf('returning true for expectation [%s], did not meet constraint[%s/%s]', name, fact, expectation))
167
+ @logger.info(sprintf('returning true for expectation [%s], did not meet constraint[%s/%s]', name, fact, expectation))
76
168
  return true
77
169
  end
78
170
  end
@@ -92,6 +184,14 @@ class Rouster
92
184
  local = true
93
185
  elsif properties.nil?
94
186
  local = false
187
+ elsif v.to_s.match(/symlink|link/)
188
+ if expectations[:target].nil?
189
+ # don't validate the link path, just check whether we're a link
190
+ local = properties[:symlink?]
191
+ else
192
+ # validate the link path
193
+ local = properties[:target].eql?(expectations[:target])
194
+ end
95
195
  else
96
196
  case v
97
197
  when 'dir', 'directory'
@@ -139,6 +239,18 @@ class Rouster
139
239
  end
140
240
  next if local.false?
141
241
  end
242
+ when :notcontains, :doesntcontain # TODO determine the appropriate attribute title here
243
+ v = v.class.eql?(Array) ? v : [v]
244
+ v.each do |regex|
245
+ local = true
246
+ begin
247
+ self.run(sprintf("grep -c '%s' %s", regex, name))
248
+ local = false
249
+ rescue
250
+ local = true
251
+ end
252
+ next if local.false?
253
+ end
142
254
  when :mode, :permissions
143
255
  if properties.nil?
144
256
  local = false
@@ -171,6 +283,8 @@ class Rouster
171
283
  end
172
284
  when :type
173
285
  # noop allowing parse_catalog() output to be passed directly
286
+ when :target
287
+ # noop allowing ensure => 'link' / 'symlink' to specify their .. target
174
288
  else
175
289
  raise InternalError.new(sprintf('unknown expectation[%s / %s]', k, v))
176
290
  end
@@ -179,9 +293,8 @@ class Rouster
179
293
  results[k] = local
180
294
  end
181
295
 
182
- @log.info(results)
296
+ @logger.info("#{name} [#{expectations}] => #{results}")
183
297
  results.find{|k,v| v.false? }.nil?
184
-
185
298
  end
186
299
 
187
300
  ##
@@ -227,7 +340,7 @@ class Rouster
227
340
  expectations[:constrain].each do |constraint|
228
341
  fact, expectation = constraint.split("\s")
229
342
  unless meets_constraint?(fact, expectation)
230
- @log.info(sprintf('returning true for expectation [%s], did not meet constraint[%s/%s]', name, fact, expectation))
343
+ @logger.info(sprintf('returning true for expectation [%s], did not meet constraint[%s/%s]', name, fact, expectation))
231
344
  return true
232
345
  end
233
346
  end
@@ -276,7 +389,7 @@ class Rouster
276
389
  results[k] = local
277
390
  end
278
391
 
279
- @log.info(results)
392
+ @logger.info("#{name} [#{expectations}] => #{results}")
280
393
  results.find{|k,v| v.false? }.nil?
281
394
  end
282
395
 
@@ -322,7 +435,7 @@ class Rouster
322
435
  expectations[:constrain].each do |constraint|
323
436
  fact, expectation = constraint.split("\s")
324
437
  unless meets_constraint?(fact, expectation)
325
- @log.info(sprintf('returning true for expectation [%s], did not meet constraint[%s/%s]', name, fact, expectation))
438
+ @logger.info(sprintf('returning true for expectation [%s], did not meet constraint[%s/%s]', name, fact, expectation))
326
439
  return true
327
440
  end
328
441
  end
@@ -368,8 +481,8 @@ class Rouster
368
481
  end
369
482
 
370
483
  # TODO figure out a good way to allow access to the entire hash, not just boolean -- for now just print at an info level
371
- @log.info(results)
372
484
 
485
+ @logger.info("#{name} [#{expectations}] => #{results}")
373
486
  results.find{|k,v| v.false? }.nil?
374
487
  end
375
488
 
@@ -417,7 +530,7 @@ class Rouster
417
530
  expectations[:constrain].each do |constraint|
418
531
  fact, expectation = constraint.split("\s")
419
532
  unless meets_constraint?(fact, expectation)
420
- @log.info(sprintf('returning true for expectation [%s], did not meet constraint[%s/%s]', name, fact, expectation))
533
+ @logger.info(sprintf('returning true for expectation [%s], did not meet constraint[%s/%s]', name, fact, expectation))
421
534
  return true
422
535
  end
423
536
  end
@@ -461,8 +574,7 @@ class Rouster
461
574
  results[k] = local
462
575
  end
463
576
 
464
- @log.info(results)
465
-
577
+ @logger.info("#{name} [#{expectations}] => #{results}")
466
578
  results.find{|k,v| v.false? }.nil?
467
579
  end
468
580
 
@@ -503,7 +615,7 @@ class Rouster
503
615
  expectations[:constrain].each do |constraint|
504
616
  fact, expectation = constraint.split("\s")
505
617
  unless meets_constraint?(fact, expectation)
506
- @log.info(sprintf('returning true for expectation [%s], did not meet constraint[%s/%s]', name, fact, expectation))
618
+ @logger.info(sprintf('returning true for expectation [%s], did not meet constraint[%s/%s]', name, fact, expectation))
507
619
  return true
508
620
  end
509
621
  end
@@ -542,9 +654,8 @@ class Rouster
542
654
  results[k] = local
543
655
  end
544
656
 
545
- @log.info(results)
657
+ @logger.info("#{name} [#{expectations}] => #{results}")
546
658
  results.find{|k,v| v.false? }.nil?
547
-
548
659
  end
549
660
 
550
661
  ##
@@ -594,7 +705,7 @@ class Rouster
594
705
  expectations[:constrain].each do |constraint|
595
706
  fact, expectation = constraint.split("\s")
596
707
  unless meets_constraint?(fact, expectation)
597
- @log.info(sprintf('returning true for expectation [%s], did not meet constraint[%s/%s]', name, fact, expectation))
708
+ @logger.info(sprintf('returning true for expectation [%s], did not meet constraint[%s/%s]', name, fact, expectation))
598
709
  return true
599
710
  end
600
711
  end
@@ -663,9 +774,8 @@ class Rouster
663
774
  results[k] = local
664
775
  end
665
776
 
666
- @log.info(results)
777
+ @logger.info("#{name} [#{expectations}] => #{results}")
667
778
  results.find{|k,v| v.false? }.nil?
668
-
669
779
  end
670
780
 
671
781
  ## internal methods
@@ -687,7 +797,7 @@ class Rouster
687
797
 
688
798
  unless self.respond_to?('facter') or self.respond_to?('hiera')
689
799
  # if we haven't loaded puppet.rb, we won't have access to facts/hiera lookups
690
- @log.warn('using constraints without loading [rouster/puppet] will not work, forcing no-op')
800
+ @logger.warn('using constraints without loading [rouster/puppet] will not work, forcing no-op')
691
801
  return false
692
802
  end
693
803
 
@@ -712,7 +822,7 @@ class Rouster
712
822
  res = ! actual.to_s.match(/#{expectation}/).nil?
713
823
  end
714
824
 
715
- @log.debug(sprintf('meets_constraint?(%s, %s): %s', key, expectation, res.nil?))
825
+ @logger.debug(sprintf('meets_constraint?(%s, %s): %s', key, expectation, res.nil?))
716
826
  res
717
827
  end
718
828