rouster 0.53 → 0.57

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