rouster 0.7 → 0.41

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -3
  3. data/README.md +7 -241
  4. data/Rakefile +18 -55
  5. data/Vagrantfile +8 -26
  6. data/lib/rouster.rb +183 -404
  7. data/lib/rouster/deltas.rb +118 -577
  8. data/lib/rouster/puppet.rb +34 -209
  9. data/lib/rouster/testing.rb +59 -366
  10. data/lib/rouster/tests.rb +19 -70
  11. data/path_helper.rb +7 -5
  12. data/rouster.gemspec +1 -3
  13. data/test/basic.rb +1 -4
  14. data/test/functional/deltas/test_get_groups.rb +2 -74
  15. data/test/functional/deltas/test_get_packages.rb +4 -86
  16. data/test/functional/deltas/test_get_ports.rb +1 -26
  17. data/test/functional/deltas/test_get_services.rb +4 -43
  18. data/test/functional/deltas/test_get_users.rb +2 -35
  19. data/test/functional/puppet/test_facter.rb +1 -41
  20. data/test/functional/puppet/test_get_puppet_star.rb +68 -0
  21. data/test/functional/test_caching.rb +1 -5
  22. data/test/functional/test_dirs.rb +0 -25
  23. data/test/functional/test_get.rb +6 -10
  24. data/test/functional/test_inspect.rb +1 -1
  25. data/test/functional/test_is_file.rb +1 -17
  26. data/test/functional/test_new.rb +22 -233
  27. data/test/functional/test_put.rb +11 -9
  28. data/test/functional/test_restart.rb +4 -1
  29. data/test/functional/test_run.rb +3 -2
  30. data/test/puppet/test_apply.rb +11 -13
  31. data/test/puppet/test_roles.rb +173 -0
  32. data/test/unit/test_new.rb +0 -88
  33. data/test/unit/test_parse_ls_string.rb +0 -67
  34. data/test/unit/testing/test_validate_file.rb +47 -39
  35. data/test/unit/testing/test_validate_package.rb +10 -36
  36. metadata +6 -46
  37. data/.reek +0 -63
  38. data/.travis.yml +0 -11
  39. data/Gemfile +0 -17
  40. data/Gemfile.lock +0 -102
  41. data/LICENSE +0 -9
  42. data/examples/aws.rb +0 -85
  43. data/examples/openstack.rb +0 -61
  44. data/examples/passthrough.rb +0 -71
  45. data/lib/rouster/vagrant.rb +0 -311
  46. data/plugins/aws.rb +0 -347
  47. data/plugins/openstack.rb +0 -136
  48. data/test/functional/deltas/test_get_crontab.rb +0 -161
  49. data/test/functional/deltas/test_get_os.rb +0 -68
  50. data/test/functional/test_is_in_file.rb +0 -40
  51. data/test/functional/test_passthroughs.rb +0 -94
  52. data/test/functional/test_validate_file.rb +0 -131
  53. data/test/unit/puppet/resources/puppet_run_with_failed_exec +0 -59
  54. data/test/unit/puppet/resources/puppet_run_with_successful_exec +0 -61
  55. data/test/unit/puppet/test_get_puppet_star.rb +0 -91
  56. data/test/unit/puppet/test_puppet_parsing.rb +0 -44
  57. data/test/unit/testing/resources/osx-launchd +0 -285
  58. data/test/unit/testing/resources/rhel-systemd +0 -46
  59. data/test/unit/testing/resources/rhel-systemv +0 -41
  60. data/test/unit/testing/resources/rhel-upstart +0 -20
  61. data/test/unit/testing/test_get_services.rb +0 -178
  62. data/test/unit/testing/test_validate_cron.rb +0 -78
  63. data/test/unit/testing/test_validate_port.rb +0 -103
@@ -5,6 +5,8 @@ require 'net/https'
5
5
  require 'socket'
6
6
  require 'uri'
7
7
 
8
+ # TODO use @cache_timeout to invalidate data cached here
9
+
8
10
  class Rouster
9
11
 
10
12
  ##
@@ -16,17 +18,8 @@ class Rouster
16
18
  # * [cache] - whether to store/return cached facter data, if available
17
19
  # * [custom_facts] - whether to include custom facts in return (uses -p argument)
18
20
  def facter(cache=true, custom_facts=true)
19
-
20
21
  if cache.true? and ! self.facts.nil?
21
-
22
- if self.cache_timeout and self.cache_timeout.is_a?(Integer) and (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
- self.facts = nil
25
- else
26
- @logger.debug(sprintf('using cached [facter] from [%s]', self.cache[:facter]))
27
- return self.facts
28
- end
29
-
22
+ return self.facts
30
23
  end
31
24
 
32
25
  raw = self.run(sprintf('facter %s', custom_facts.true? ? '-p' : ''))
@@ -38,35 +31,12 @@ class Rouster
38
31
  end
39
32
 
40
33
  if cache.true?
41
- @logger.debug(sprintf('caching [facter] at [%s]', Time.now.asctime))
42
34
  self.facts = res
43
- self.cache[:facter] = Time.now.to_i
44
35
  end
45
36
 
46
37
  res
47
38
  end
48
39
 
49
- ##
50
- # did_exec_fire?
51
- #
52
- # given the name of an Exec resource, parse the output from the most recent puppet run
53
- # and return true/false based on whether the exec in question was fired
54
- def did_exec_fire?(resource_name, puppet_run = self.last_puppet_run)
55
- # Notice: /Stage[main]//Exec[foo]/returns: executed successfully
56
- # Error: /Stage[main]//Exec[bar]/returns: change from notrun to 0 failed: Could not find command '/bin/bar'
57
- matchers = [
58
- 'Notice: /Stage\[.*\]//Exec\[%s\]/returns: executed successfully',
59
- 'Error: /Stage\[.*\]//Exec\[%s\]/returns: change from notrun to 0 failed'
60
- ]
61
-
62
- matchers.each do |m|
63
- matcher = sprintf(m, resource_name)
64
- return true if puppet_run.match(matcher)
65
- end
66
-
67
- false
68
- end
69
-
70
40
  ##
71
41
  # get_catalog
72
42
  #
@@ -83,11 +53,7 @@ class Rouster
83
53
  # post https://<puppetmaster>/catalog/<node>?facts_format=pson&facts=<pson URL encoded> == ht to patrick@puppetlabs
84
54
  certname = hostname.nil? ? self.run('hostname --fqdn').chomp : hostname
85
55
  puppetmaster = puppetmaster.nil? ? 'puppet' : puppetmaster
86
- facts = facts.nil? ? self.facter() : facts
87
-
88
- %w(fqdn hostname operatingsystem operatingsystemrelease osfamily rubyversion).each do |required|
89
- raise ArgumentError.new(sprintf('missing required fact[%s]', required)) unless facts.has_key?(required)
90
- end
56
+ facts = facts.nil? ? self.facter() : facts # TODO check for presence of certain 'required' facts/datatype?
91
57
 
92
58
  raise InternalError.new('need to finish conversion of facts to PSON')
93
59
  facts.to_pson # this does not work, but needs to
@@ -114,17 +80,8 @@ class Rouster
114
80
  # parameters
115
81
  # * [input] - string to look at, defaults to self.get_output()
116
82
  def get_puppet_errors(input=nil)
117
- str = input.nil? ? self.get_output() : input
118
- errors = nil
119
- errors_27 = str.scan(/35merr:.*/)
120
- errors_30 = str.scan(/Error:.*/)
121
-
122
- # TODO this is a little less than efficient, don't scan for 3.0 if you found 2.7
123
- if errors_27.size > 0
124
- errors = errors_27
125
- else
126
- errors = errors_30
127
- end
83
+ str = input.nil? ? self.get_output() : input
84
+ errors = str.scan(/35merr:.*/)
128
85
 
129
86
  errors.empty? ? nil : errors
130
87
  end
@@ -137,17 +94,8 @@ class Rouster
137
94
  # parameters
138
95
  # * [input] - string to look at, defaults to self.get_output()
139
96
  def get_puppet_notices(input=nil)
140
- str = input.nil? ? self.get_output() : input
141
- notices = nil
142
- notices_27 = str.scan(/36mnotice:.*/) # not sure when this stopped working
143
- notices_30 = str.scan(/Notice:.*/)
144
-
145
- # TODO this is a little less than efficient, don't scan for 3.0 if you found 2.7
146
- if notices_27.size > 0
147
- notices = notices_27
148
- else
149
- notices = notices_30
150
- end
97
+ str = input.nil? ? self.get_output() : input
98
+ notices = str.scan(/36mnotice:.*/)
151
99
 
152
100
  notices.empty? ? nil : notices
153
101
  end
@@ -176,41 +124,13 @@ class Rouster
176
124
  # returns hiera results from self
177
125
  #
178
126
  # parameters
179
- # * <key> - hiera key to look up
180
- # * [facts] - hash of facts to be used in hiera lookup (technically optional, but most useful hiera lookups are based on facts)
181
- # * [config] - path to hiera configuration -- this is only optional if you have a hiera.yaml file in ~/vagrant, default option is correct for most puppet installations
182
- # * [options] - any additional parameters to be passed to hiera directly
183
- #
184
- # note
185
- # * if no facts are provided, facter() will be called - to really run hiera without facts, send an empty hash
186
- # * this method is mostly useful on your puppet master, as your agents won't likely have /etc/puppet/hiera.yaml - to get data on another node, specify it's facts and call hiera on your ppm
187
- def hiera(key, facts=nil, config='/etc/puppet/hiera.yaml', options=nil)
188
- # TODO implement caching? where do we keep it? self.hiera{}? or self.deltas{} -- leaning towards #1
189
-
190
- cmd = 'hiera'
127
+ # * <key> - hiera key to look up
128
+ # * [config] - path to hiera configuration -- this is only optional if you have a hiera.yaml file in ~/vagrant
129
+ def hiera(key, config=nil)
191
130
 
192
- if facts.nil?
193
- @logger.info('no facts provided, calling facter() automatically')
194
- facts = self.facter()
195
- end
196
-
197
- if facts.keys.size > 0
198
- scope_file = sprintf('/tmp/rouster-hiera_scope.%s.%s.json', $$, Time.now.to_i)
131
+ # TODO implement this
132
+ raise NotImplementedError.new()
199
133
 
200
- File.write(scope_file, facts.to_json)
201
- self.put(scope_file, scope_file)
202
- File.delete(scope_file)
203
-
204
- cmd << sprintf(' -j %s', scope_file)
205
- end
206
-
207
- cmd << sprintf(' -c %s', config) unless config.nil?
208
- cmd << sprintf(' %s', options) unless options.nil?
209
- cmd << sprintf(' %s', key)
210
-
211
- raw = self.run(cmd)
212
-
213
- JSON.parse(raw)
214
134
  end
215
135
 
216
136
  ##
@@ -310,6 +230,7 @@ class Rouster
310
230
  end
311
231
 
312
232
  # remove all nil references
233
+ # TODO make this more rubyish
313
234
  resources.each_key do |name|
314
235
  resources[name].each_pair do |k,v|
315
236
  unless v
@@ -318,14 +239,13 @@ class Rouster
318
239
  end
319
240
  end
320
241
 
242
+
321
243
  results[:classes] = classes
322
244
  results[:resources] = resources
323
245
 
324
246
  results
325
247
  end
326
248
 
327
- # TODO: come up with better method names here.. remove_existing_certs() and remove_specific_cert() are not very descriptive
328
-
329
249
  ##
330
250
  # remove_existing_certs
331
251
  #
@@ -334,57 +254,16 @@ class Rouster
334
254
  #
335
255
  # parameters
336
256
  # * <puppetmaster> - string/partial regex of certificate names to keep
337
- def remove_existing_certs (except)
338
- except = except.kind_of?(Array) ? except : [except] # need to move from <>.class.eql? to <>.kind_of? in a number of places
339
- hosts = Array.new()
340
-
341
- res = self.run('puppet cert list --all')
342
-
343
- # TODO refactor this away from the hacky_break
344
- res.each_line do |line|
345
- hacky_break = false
346
-
347
- except.each do |exception|
348
- next if hacky_break
349
- hacky_break = line.match(/#{exception}/)
350
- end
351
-
352
- next if hacky_break
353
-
354
- host = $1 if line.match(/^\+\s"(.*?)"/)
355
-
356
- hosts.push(host) unless host.nil? # only want to clear signed certs
357
- end
358
-
359
- hosts.each do |host|
360
- self.run(sprintf('puppet cert --clean %s', host))
361
- end
362
-
363
- end
364
-
365
- ##
366
- # remove_specific_cert
367
- #
368
- # ... 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
369
- def remove_specific_cert (targets)
370
- targets = targets.kind_of?(Array) ? targets : [targets]
257
+ def remove_existing_certs (puppetmaster)
371
258
  hosts = Array.new()
372
259
 
373
260
  res = self.run('puppet cert list --all')
374
261
 
375
262
  res.each_line do |line|
376
- hacky_break = true
377
-
378
- targets.each do |target|
379
- next unless hacky_break
380
- hacky_break = line.match(/#{target}/)
381
- end
382
-
383
- next unless hacky_break
384
-
263
+ next if line.match(/#{puppetmaster}/)
385
264
  host = $1 if line.match(/^\+\s"(.*?)"/)
386
- hosts.push(host) unless host.nil?
387
265
 
266
+ hosts.push(host)
388
267
  end
389
268
 
390
269
  hosts.each do |host|
@@ -393,22 +272,15 @@ class Rouster
393
272
 
394
273
  end
395
274
 
396
-
397
275
  ##
398
276
  # run_puppet
399
277
  #
400
278
  # ... runs puppet on self, returns nothing
401
279
  #
402
280
  # currently supports 2 methods of running puppet:
403
- # * master - runs 'puppet agent -t'
281
+ # * master - runs '/sbin/service puppet once -t'
404
282
  # * supported options
405
283
  # * expected_exitcode - string/integer/array of acceptable exit code(s)
406
- # * configtimeout - string/integer of the acceptable configtimeout value
407
- # * environment - string of the environment to use
408
- # * certname - string of the certname to use in place of the host fqdn
409
- # * pluginsync - bool value if pluginsync should be used
410
- # * server - string value of the puppetmasters fqdn / ip
411
- # * additional_options - string of various options that would be passed to puppet
412
284
  # * masterless - runs 'puppet apply <options>' after determining version of puppet running and adjusting arguments
413
285
  # * supported options
414
286
  # * expected_exitcode - string/integer/array of acceptable exit code(s)
@@ -416,80 +288,41 @@ class Rouster
416
288
  # * manifest_file - string/array of strings of paths to manifest(s) to apply
417
289
  # * manifest_dir - string/array of strings of directories containing manifest(s) to apply - is recursive
418
290
  # * module_dir - path to module directory -- currently a required parameter, is this correct?
419
- # * environment - string of the environment to use (default: production)
420
- # * certname - string of the certname to use in place of the host fqdn (default: unused)
421
- # * pluginsync - bool value if pluginsync should be used (default: true)
422
- # * additional_options - string of various options that would be passed to puppet
423
291
  #
424
292
  # parameters
425
293
  # * [mode] - method to run puppet, defaults to 'master'
426
294
  # * [opts] - hash of additional options
427
- def run_puppet(mode='master', passed_opts={})
295
+ def run_puppet(mode='master', passed_opts=nil)
428
296
 
429
297
  if mode.eql?('master')
430
298
  opts = {
431
- :expected_exitcode => 0,
432
- :configtimeout => nil,
433
- :environment => nil,
434
- :certname => nil,
435
- :server => nil,
436
- :pluginsync => false,
437
- :additional_options => nil
299
+ :expected_exitcode => 0
438
300
  }.merge!(passed_opts)
439
301
 
440
- cmd = 'puppet agent -t'
441
- cmd << sprintf(' --configtimeout %s', opts[:configtimeout]) unless opts[:configtimeout].nil?
442
- cmd << sprintf(' --environment %s', opts[:environment]) unless opts[:environment].nil?
443
- cmd << sprintf(' --certname %s', opts[:certname]) unless opts[:certname].nil?
444
- cmd << sprintf(' --server %s', opts[:server]) unless opts[:server].nil?
445
- cmd << ' --pluginsync' if opts[:pluginsync]
446
- cmd << opts[:additional_options] unless opts[:additional_options].nil?
447
-
448
- self.run(cmd, opts[:expected_exitcode])
302
+ self.run('/sbin/service puppet once -t', opts[:expected_exitcode])
449
303
 
450
304
  elsif mode.eql?('masterless')
451
305
  opts = {
452
- :expected_exitcode => 2,
453
- :hiera_config => nil,
454
- :manifest_file => nil, # can be a string or array, will 'puppet apply' each
455
- :manifest_dir => nil, # can be a string or array, will 'puppet apply' each module in the dir (recursively)
456
- :module_dir => nil,
457
- :environment => nil,
458
- :certname => nil,
459
- :pluginsync => false,
460
- :additional_options => nil
306
+ :expected_exitcode => 2,
307
+ :hiera_config => nil,
308
+ :manifest_file => nil, # can be a string or array, will 'puppet apply' each
309
+ :manifest_dir => nil, # can be a string or array, will 'puppet apply' each module in the dir (recursively)
310
+ :module_dir => nil
461
311
  }.merge!(passed_opts)
462
312
 
463
- ## validate arguments -- can do better here (:manifest_dir, :manifest_file)
464
- puppet_version = self.get_puppet_version() # hiera_config specification is only supported in >3.0, but NOT required anywhere
313
+ ## validate required arguments
314
+ raise InternalError.new(sprintf('invalid hiera config specified[%s]', opts[:hiera_config])) unless self.is_file?(opts[:hiera_config])
315
+ raise InternalError.new(sprintf('invalid module dir specified[%s]', opts[:module_dir])) unless self.is_dir?(opts[:module_dir])
465
316
 
466
- if opts[:hiera_config]
467
- if puppet_version > '3.0'
468
- raise InternalError.new(sprintf('invalid hiera config specified[%s]', opts[:hiera_config])) unless self.is_file?(opts[:hiera_config])
469
- else
470
- @logger.error(sprintf('puppet version[%s] does not support --hiera_config, ignoring', puppet_version))
471
- end
472
- end
473
-
474
- if opts[:module_dir]
475
- raise InternalError.new(sprintf('invalid module dir specified[%s]', opts[:module_dir])) unless self.is_dir?(opts[:module_dir])
476
- end
317
+ puppet_version = self.get_puppet_version() # hiera_config specification is only supported in >3.0
477
318
 
478
319
  if opts[:manifest_file]
479
320
  opts[:manifest_file] = opts[:manifest_file].class.eql?(Array) ? opts[:manifest_file] : [opts[:manifest_file]]
480
321
  opts[:manifest_file].each do |file|
481
322
  raise InternalError.new(sprintf('invalid manifest file specified[%s]', file)) unless self.is_file?(file)
482
323
 
483
- cmd = 'puppet apply --detailed-exitcodes'
484
- cmd << sprintf(' --modulepath=%s', opts[:module_dir]) unless opts[:module_dir].nil?
485
- cmd << sprintf(' --hiera_config=%s', opts[:hiera_config]) unless opts[:hiera_config].nil? or puppet_version < '3.0'
486
- cmd << sprintf(' --environment %s', opts[:environment]) unless opts[:environment].nil?
487
- cmd << sprintf(' --certname %s', opts[:certname]) unless opts[:certname].nil?
488
- cmd << ' --pluginsync' if opts[:pluginsync]
489
- cmd << sprintf(' %s', opts[:additional_options]) unless opts[:additional_options].nil?
490
- cmd << sprintf(' %s', file)
324
+ self.run(sprintf('puppet apply %s --modulepath=%s %s', (puppet_version > '3.0') ? "--hiera_config=#{opts[:hiera_config]}" : '', opts[:module_dir], file), opts[:expected_exitcode])
491
325
 
492
- self.last_puppet_run = self.run(cmd, opts[:expected_exitcode])
493
326
  end
494
327
  end
495
328
 
@@ -502,16 +335,8 @@ class Rouster
502
335
 
503
336
  manifests.each do |m|
504
337
 
505
- cmd = 'puppet apply --detailed-exitcodes'
506
- cmd << sprintf(' --modulepath=%s', opts[:module_dir]) unless opts[:module_dir].nil?
507
- cmd << sprintf(' --hiera_config=%s', opts[:hiera_config]) unless opts[:hiera_config].nil? or puppet_version < '3.0'
508
- cmd << sprintf(' --environment %s', opts[:environment]) unless opts[:environment].nil?
509
- cmd << sprintf(' --certname %s', opts[:certname]) unless opts[:certname].nil?
510
- cmd << ' --pluginsync' if opts[:pluginsync]
511
- cmd << sprintf(' %s', opts[:additional_options]) unless opts[:additional_options].nil?
512
- cmd << sprintf(' %s', m)
338
+ self.run(sprintf('puppet apply %s --modulepath=%s %s', (puppet_version > '3.0') ? "--hiera_config=#{opts[:hiera_config]}" : '', opts[:module_dir], m), opts[:expected_exitcode])
513
339
 
514
- self.last_puppet_run = self.run(cmd, opts[:expected_exitcode])
515
340
  end
516
341
 
517
342
  end
@@ -524,4 +349,4 @@ class Rouster
524
349
 
525
350
  end
526
351
 
527
- end
352
+ end
@@ -5,103 +5,6 @@ require 'rouster/deltas'
5
5
 
6
6
  class Rouster
7
7
 
8
- ##
9
- # validate_cron
10
- #
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
104
-
105
8
  ##
106
9
  # validate_file
107
10
  #
@@ -110,7 +13,6 @@ class Rouster
110
13
  # parameters
111
14
  # * <name> - full file name or relative (to ~vagrant)
112
15
  # * <expectations> - hash of expectations, see examples
113
- # * <fail_fast> - return false immediately on any failure (default is false)
114
16
  #
115
17
  # example expectations:
116
18
  # '/sys/kernel/mm/redhat_transparent_hugepage/enabled', {
@@ -144,9 +46,9 @@ class Rouster
144
46
  # * :owner
145
47
  # * :group
146
48
  # * :constrain
147
- def validate_file(name, expectations, fail_fast=false, cache=false)
49
+ def validate_file(name, expectations, cache=false)
148
50
 
149
- if expectations[:ensure].nil? and expectations[:exists].nil? and expectations[:directory].nil? and expectations[:file?].nil?
51
+ if expectations[:ensure].nil? and expectations[:exists].nil?
150
52
  expectations[:ensure] = 'file'
151
53
  end
152
54
 
@@ -154,17 +56,9 @@ class Rouster
154
56
  expectations[:constrain] = expectations[:constrain].class.eql?(Array) ? expectations[:constrain] : [expectations[:constrain]]
155
57
 
156
58
  expectations[:constrain].each do |constraint|
157
- valid = constraint.match(/^(\S+?)\s(.*)$/)
158
-
159
- if valid.nil?
160
- raise InternalError.new(sprintf('invalid constraint[%s] specified', constraint))
161
- end
162
-
163
- fact = $1
164
- expectation = $2
165
-
59
+ fact, expectation = constraint.split("\s")
166
60
  unless meets_constraint?(fact, expectation)
167
- @logger.info(sprintf('returning true for expectation [%s], did not meet constraint[%s/%s]', name, fact, expectation))
61
+ @log.info(sprintf('returning true for expectation [%s], did not meet constraint[%s/%s]', name, fact, expectation))
168
62
  return true
169
63
  end
170
64
  end
@@ -172,7 +66,7 @@ class Rouster
172
66
  expectations.delete(:constrain)
173
67
  end
174
68
 
175
- properties = (expectations[:ensure].eql?('file')) ? self.file(name, cache) : self.dir(name, cache)
69
+ properties = (! expectations[:ensure].eql?('file')) ? self.file(name, cache) : self.dir(name, cache)
176
70
  results = Hash.new()
177
71
  local = nil
178
72
 
@@ -184,14 +78,6 @@ class Rouster
184
78
  local = true
185
79
  elsif properties.nil?
186
80
  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
195
81
  else
196
82
  case v
197
83
  when 'dir', 'directory'
@@ -223,7 +109,7 @@ class Rouster
223
109
  if properties[:directory?]
224
110
  local = v.to_s.match(/absent|false/).nil?
225
111
  else
226
- local = ! v.to_s.match(/absent|false/).nil?
112
+ local = true
227
113
  end
228
114
  else
229
115
  local = false
@@ -234,22 +120,10 @@ class Rouster
234
120
  local = true
235
121
  begin
236
122
  self.run(sprintf("grep -c '%s' %s", regex, name))
237
- rescue => e
123
+ rescue
238
124
  local = false
239
125
  end
240
- break if local.false?
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 => e
250
- local = true
251
- end
252
- break if local.false?
126
+ next if local.false?
253
127
  end
254
128
  when :mode, :permissions
255
129
  if properties.nil?
@@ -282,19 +156,17 @@ class Rouster
282
156
  local = false
283
157
  end
284
158
  when :type
285
- # noop allowing parse_catalog() output to be passed directly
286
- when :target
287
- # noop allowing ensure => 'link' / 'symlink' to specify their .. target
159
+ # noop
288
160
  else
289
161
  raise InternalError.new(sprintf('unknown expectation[%s / %s]', k, v))
290
162
  end
291
163
 
292
- return false if local.eql?(false) and fail_fast.eql?(true)
293
164
  results[k] = local
294
165
  end
295
166
 
296
- @logger.info("#{name} [#{expectations}] => #{results}")
167
+ @log.info(results)
297
168
  results.find{|k,v| v.false? }.nil?
169
+
298
170
  end
299
171
 
300
172
  ##
@@ -305,7 +177,6 @@ class Rouster
305
177
  # paramaters
306
178
  # * <name> - group name
307
179
  # * <expectations> - hash of expectations, see examples
308
- # * <fail_fast> - return false immediately on any failure (default is false)
309
180
  #
310
181
  # example expectations:
311
182
  # 'root', {
@@ -327,7 +198,7 @@ class Rouster
327
198
  # * :gid
328
199
  # * :user|:users (string or array)
329
200
  # * :constrain
330
- def validate_group(name, expectations, fail_fast=false)
201
+ def validate_group(name, expectations)
331
202
  groups = self.get_groups(true)
332
203
 
333
204
  if expectations[:ensure].nil? and expectations[:exists].nil?
@@ -340,7 +211,7 @@ class Rouster
340
211
  expectations[:constrain].each do |constraint|
341
212
  fact, expectation = constraint.split("\s")
342
213
  unless meets_constraint?(fact, expectation)
343
- @logger.info(sprintf('returning true for expectation [%s], did not meet constraint[%s/%s]', name, fact, expectation))
214
+ @log.info(sprintf('returning true for expectation [%s], did not meet constraint[%s/%s]', name, fact, expectation))
344
215
  return true
345
216
  end
346
217
  end
@@ -364,33 +235,25 @@ class Rouster
364
235
  local = v.to_s.match(/absent|false/).nil? ? false : true
365
236
  end
366
237
  when :gid
367
- if groups[name].is_a?(Hash) and groups[name].has_key?(:gid)
368
- local = v.to_s.eql?(groups[name][:gid].to_s)
369
- else
370
- local = false
371
- end
238
+ local = v.to_s.eql?(groups[name][:gid].to_s)
372
239
  when :user, :users
373
240
  v = v.class.eql?(Array) ? v : [v]
374
241
  v.each do |user|
375
- if groups[name].is_a?(Hash) and groups[name].has_key?(:users)
376
- local = groups[name][:users].member?(user)
377
- else
378
- local = false
379
- end
242
+ local = groups[name][:users].member?(user)
380
243
  break unless local.true? # need to make the return value smarter if we want to store data on which user failed
381
244
  end
382
245
  when :type
383
- # noop allowing parse_catalog() output to be passed directly
246
+ # noop
384
247
  else
385
248
  raise InternalError.new(sprintf('unknown expectation[%s / %s]', k, v))
386
249
  end
387
250
 
388
- return false if local.eql?(false) and fail_fast.eql?(true)
389
251
  results[k] = local
390
252
  end
391
253
 
392
- @logger.info("#{name} [#{expectations}] => #{results}")
254
+ @log.info(results.pretty_print_inspect())
393
255
  results.find{|k,v| v.false? }.nil?
256
+
394
257
  end
395
258
 
396
259
  ##
@@ -401,7 +264,6 @@ class Rouster
401
264
  # parameters
402
265
  # * <name> - package name
403
266
  # * <expectations> - hash of expectations, see examples
404
- # * <fail_fast> - return false immediately on any failure (default is false)
405
267
  #
406
268
  # example expectations:
407
269
  # 'perl-Net-SNMP', {
@@ -422,7 +284,7 @@ class Rouster
422
284
  # * :exists|ensure
423
285
  # * :version (literal or basic comparison)
424
286
  # * :constrain
425
- def validate_package(name, expectations, fail_fast=false)
287
+ def validate_package(name, expectations)
426
288
  packages = self.get_packages(true)
427
289
 
428
290
  if expectations[:ensure].nil? and expectations[:exists].nil?
@@ -435,7 +297,7 @@ class Rouster
435
297
  expectations[:constrain].each do |constraint|
436
298
  fact, expectation = constraint.split("\s")
437
299
  unless meets_constraint?(fact, expectation)
438
- @logger.info(sprintf('returning true for expectation [%s], did not meet constraint[%s/%s]', name, fact, expectation))
300
+ @log.info(sprintf('returning true for expectation [%s], did not meet constraint[%s/%s]', name, fact, expectation))
439
301
  return true
440
302
  end
441
303
  end
@@ -459,148 +321,26 @@ class Rouster
459
321
  local = v.to_s.match(/absent|false/).nil? ? false : true
460
322
  end
461
323
  when :version
462
- # TODO support determination based on multiple versions of the same package installed (?)
463
- if packages.has_key?(name)
464
-
465
- lps = packages[name].is_a?(Array) ? packages[name] : [ packages[name] ]
466
-
467
- lps.each do |lp|
468
- if v.split("\s").size > 1
469
- ## generic comparator functionality
470
- comp, expectation = v.split("\s")
471
- local = generic_comparator(lp[:version], comp, expectation)
472
- break unless local.eql?(true)
473
- else
474
- local = ! v.to_s.match(/#{lp[:version]}/).nil?
475
- break unless local.eql?(true)
476
- end
477
- end
324
+ if v.split("\s").size > 1
325
+ ## generic comparator functionality
326
+ comp, expectation = v.split("\s")
327
+ local = generic_comparator(packages[name], comp, expectation)
478
328
  else
479
- local = false
480
- end
481
- when :arch, :architecture
482
- if packages.has_key?(name)
483
- archs = []
484
- lps = packages[name].is_a?(Array) ? packages[name] : [ packages[name] ]
485
- lps.each { |p| archs << p[:arch] }
486
- if v.is_a?(Array)
487
- v.each do |arch|
488
- local = archs.member?(arch)
489
- break unless local.eql?(true) # fail fast - if we are looking for an arch that DNE, bail out
490
- end
491
- else
492
- local = archs.member?(v)
493
- end
329
+ local = ! v.to_s.match(/#{packages[name]}/).nil?
494
330
  end
495
331
  when :type
496
- # noop allowing parse_catalog() output to be passed directly
332
+ # noop
497
333
  else
498
334
  raise InternalError.new(sprintf('unknown expectation[%s / %s]', k, v))
499
335
  end
500
336
 
501
- return false if local.eql?(false) and fail_fast.eql?(true)
502
337
  results[k] = local
503
338
  end
504
339
 
505
340
  # TODO figure out a good way to allow access to the entire hash, not just boolean -- for now just print at an info level
341
+ @log.info(results)
506
342
 
507
- @logger.info("#{name} [#{expectations}] => #{results}")
508
- results.find{|k,v| v.false? }.nil?
509
- end
510
-
511
- # given a port nnumber and a hash of expectations, returns true|false whether port meets expectations
512
- #
513
- # parameters
514
- # * <number> - port number
515
- # * <expectations> - hash of expectations, see examples
516
- #
517
- # example expectations:
518
- # '22', {
519
- # :ensure => 'active',
520
- # :protocol => 'tcp',
521
- # :address => '0.0.0.0'
522
- # },
523
- #
524
- # '1234', {
525
- # :ensure => 'open',
526
- # :address => '*',
527
- # :constrain => 'is_virtual false'
528
- # }
529
- #
530
- # supported keys:
531
- # * :exists|ensure|state
532
- # * :address
533
- # * :protocol|proto
534
- # * :constrain
535
- def validate_port(number, expectations, fail_fast=false)
536
- number = number.to_s
537
- ports = self.get_ports(true)
538
-
539
- if expectations[:ensure].nil? and expectations[:exists].nil? and expectations[:state].nil?
540
- expectations[:ensure] = 'present'
541
- end
542
-
543
- if expectations[:protocol].nil? and expectations[:proto].nil?
544
- expectations[:protocol] = 'tcp'
545
- elsif ! expectations[:proto].nil?
546
- expectations[:protocol] = expectations[:proto]
547
- end
548
-
549
- if expectations.has_key?(:constrain)
550
- expectations[:constrain] = expectations[:constrain].class.eql?(Array) ? expectations[:constrain] : [expectations[:constrain]]
551
-
552
- expectations[:constrain].each do |constraint|
553
- fact, expectation = constraint.split("\s")
554
- unless meets_constraint?(fact, expectation)
555
- @logger.info(sprintf('returning true for expectation [%s], did not meet constraint[%s/%s]', name, fact, expectation))
556
- return true
557
- end
558
- end
559
-
560
- expectations.delete(:constrain)
561
- end
562
-
563
- results = Hash.new()
564
- local = nil
565
-
566
- expectations.each do |k,v|
567
- case k
568
- when :ensure, :exists, :state
569
- if v.to_s.match(/absent|false|open/)
570
- local = ports[expectations[:protocol]][number].nil?
571
- else
572
- local = ! ports[expectations[:protocol]][number].nil?
573
- end
574
- when :protocol, :proto
575
- # TODO rewrite this in a less hacky way
576
- if expectations[:ensure].to_s.match(/absent|false|open/) or expectations[:exists].to_s.match(/absent|false|open/) or expectations[:state].to_s.match(/absent|false|open/)
577
- local = true
578
- else
579
- local = ports[v].has_key?(number)
580
- end
581
-
582
- when :address
583
- lr = Array.new
584
- if ports[expectations[:protocol]][number]
585
- addresses = ports[expectations[:protocol]][number][:address]
586
- addresses.each_key do |address|
587
- lr.push(address.eql?(v.to_s))
588
- end
589
-
590
- local = ! lr.find{|e| e.true? }.nil? # this feels jankity
591
- else
592
- # this port isn't open in the first place, won't match any addresses we expect to see it on
593
- local = false
594
- end
595
- else
596
- raise InternalError.new(sprintf('unknown expectation[%s / %s]', k, v))
597
- end
598
-
599
- return false if local.eql?(false) and fail_fast.eql?(true)
600
- results[k] = local
601
- end
602
-
603
- @logger.info("#{name} [#{expectations}] => #{results}")
343
+ # TODO should we implement a fail fast method? at least an option
604
344
  results.find{|k,v| v.false? }.nil?
605
345
  end
606
346
 
@@ -612,7 +352,6 @@ class Rouster
612
352
  # parameters
613
353
  # * <name> - service name
614
354
  # * <expectations> - hash of expectations, see examples
615
- # * <fail_fast> - return false immediately on any failure (default is false)
616
355
  #
617
356
  # example expectations:
618
357
  # 'ntp', {
@@ -628,7 +367,7 @@ class Rouster
628
367
  # * :exists|:ensure
629
368
  # * :state,:status
630
369
  # * :constrain
631
- def validate_service(name, expectations, fail_fast=false)
370
+ def validate_service(name, expectations)
632
371
  services = self.get_services(true)
633
372
 
634
373
  if expectations[:ensure].nil? and expectations[:exists].nil?
@@ -641,7 +380,7 @@ class Rouster
641
380
  expectations[:constrain].each do |constraint|
642
381
  fact, expectation = constraint.split("\s")
643
382
  unless meets_constraint?(fact, expectation)
644
- @logger.info(sprintf('returning true for expectation [%s], did not meet constraint[%s/%s]', name, fact, expectation))
383
+ @log.info(sprintf('returning true for expectation [%s], did not meet constraint[%s/%s]', name, fact, expectation))
645
384
  return true
646
385
  end
647
386
  end
@@ -665,23 +404,19 @@ class Rouster
665
404
  local = v.to_s.match(/absent|false/).nil? ? false : true
666
405
  end
667
406
  when :state, :status
668
- if services.has_key?(name)
669
- local = ! v.match(/#{services[name]}/).nil?
670
- else
671
- local = false
672
- end
407
+ local = ! v.match(/#{services[name]}/).nil?
673
408
  when :type
674
- # noop allowing parse_catalog() output to be passed directly
409
+ # noop
675
410
  else
676
411
  raise InternalError.new(sprintf('unknown expectation[%s / %s]', k, v))
677
412
  end
678
413
 
679
- return false if local.eql?(false) and fail_fast.eql?(true)
680
414
  results[k] = local
681
415
  end
682
416
 
683
- @logger.info("#{name} [#{expectations}] => #{results}")
417
+ @log.info(results.pretty_print_inspect())
684
418
  results.find{|k,v| v.false? }.nil?
419
+
685
420
  end
686
421
 
687
422
  ##
@@ -692,7 +427,6 @@ class Rouster
692
427
  # parameters
693
428
  # * <name> - user name
694
429
  # * <expectations> - hash of expectations, see examples
695
- # * <fail_fast> - return false immediately on any failure (default is false)
696
430
  #
697
431
  # example expectations:
698
432
  # 'root' => {
@@ -718,7 +452,7 @@ class Rouster
718
452
  # * :uid
719
453
  # * :gid
720
454
  # * :constrain
721
- def validate_user(name, expectations, fail_fast=false)
455
+ def validate_user(name, expectations)
722
456
  users = self.get_users(true)
723
457
 
724
458
  if expectations[:ensure].nil? and expectations[:exists].nil?
@@ -731,7 +465,7 @@ class Rouster
731
465
  expectations[:constrain].each do |constraint|
732
466
  fact, expectation = constraint.split("\s")
733
467
  unless meets_constraint?(fact, expectation)
734
- @logger.info(sprintf('returning true for expectation [%s], did not meet constraint[%s/%s]', name, fact, expectation))
468
+ @log.info(sprintf('returning true for expectation [%s], did not meet constraint[%s/%s]', name, fact, expectation))
735
469
  return true
736
470
  end
737
471
  end
@@ -761,47 +495,27 @@ class Rouster
761
495
  break unless local.true?
762
496
  end
763
497
  when :gid
764
- if users[name].is_a?(Hash) and users[name].has_key?(:gid)
765
- local = v.to_i.eql?(users[name][:gid].to_i)
766
- else
767
- local = false
768
- end
498
+ local = v.to_i.eql?(users[name][:gid].to_i)
769
499
  when :home
770
- if users[name].is_a?(Hash) and users[name].has_key?(:home)
771
- local = ! v.match(/#{users[name][:home]}/).nil?
772
- else
773
- local = false
774
- end
500
+ local = ! v.match(/#{users[name][:home]}/).nil?
775
501
  when :home_exists
776
- if users[name].is_a?(Hash) and users[name].has_key?(:home_exists)
777
- local = ! v.to_s.match(/#{users[name][:home_exists].to_s}/).nil?
778
- else
779
- local = false
780
- end
502
+ local = ! v.to_s.match(/#{users[name][:home_exists].to_s}/).nil?
781
503
  when :shell
782
- if users[name].is_a?(Hash) and users[name].has_key?(:shell)
783
- local = ! v.match(/#{users[name][:shell]}/).nil?
784
- else
785
- local = false
786
- end
504
+ local = ! v.match(/#{users[name][:shell]}/).nil?
787
505
  when :uid
788
- if users[name].is_a?(Hash) and users[name].has_key?(:uid)
789
- local = v.to_i.eql?(users[name][:uid].to_i)
790
- else
791
- local = false
792
- end
506
+ local = v.to_i.eql?(users[name][:uid].to_i)
793
507
  when :type
794
- # noop allowing parse_catalog() output to be passed directly
508
+ # noop
795
509
  else
796
510
  raise InternalError.new(sprintf('unknown expectation[%s / %s]', k, v))
797
511
  end
798
512
 
799
- return false if local.eql?(false) and fail_fast.eql?(true)
800
513
  results[k] = local
801
514
  end
802
515
 
803
- @logger.info("#{name} [#{expectations}] => #{results}")
516
+ @log.info(results.pretty_print_inspect())
804
517
  results.find{|k,v| v.false? }.nil?
518
+
805
519
  end
806
520
 
807
521
  ## internal methods
@@ -814,59 +528,38 @@ class Rouster
814
528
  # gets facts from node, and if fact expectation regex matches actual fact, returns true
815
529
  #
816
530
  # parameters
817
- # * <key> - fact/hiera key to look up (actual value)
818
- # * <expectation> -
819
- # * [cache] - boolean controlling whether facter lookups are cached
820
- def meets_constraint?(key, expectation, cache=true)
821
-
822
- expectation = expectation.to_s
823
-
824
- unless self.respond_to?('facter') or self.respond_to?('hiera')
825
- # if we haven't loaded puppet.rb, we won't have access to facts/hiera lookups
826
- @logger.warn('using constraints without loading [rouster/puppet] will not work, forcing no-op')
531
+ # * <fact> - fact
532
+ # * <expectation>
533
+ # * [cache]
534
+ def meets_constraint?(fact, expectation, cache=true)
535
+
536
+ unless self.respond_to?('facter')
537
+ # if we haven't loaded puppet.rb, we won't have access to facts
538
+ @log.warn('using constraints without loading [rouster/puppet] will not work, forcing no-op')
827
539
  return false
828
540
  end
829
541
 
830
- facts = self.facter(cache)
831
- actual = nil
832
-
833
- if facts[key]
834
- actual = facts[key]
835
- else
836
- # value is not a fact, lets try to find it in hiera
837
- # TODO how to handle the fact that this will really only work on the puppetmaster
838
- actual = self.hiera(key, facts)
839
- end
542
+ expectation = expectation.to_s
543
+ facts = self.facter(cache)
840
544
 
841
545
  res = nil
842
-
843
546
  if expectation.split("\s").size > 1
844
547
  ## generic comparator functionality
845
548
  comp, expectation = expectation.split("\s")
846
- res = generic_comparator(actual, comp, expectation)
549
+
550
+ res = generic_comparator(facts[fact], comp, expectation)
551
+
847
552
  else
848
- res = ! actual.to_s.match(/#{expectation}/).nil?
553
+ res = ! expectation.match(/#{facts[fact]}/).nil?
554
+ @log.debug(sprintf('meets_constraint?(%s, %s): %s', fact, expectation, res.nil?))
849
555
  end
850
556
 
851
- @logger.debug(sprintf('meets_constraint?(%s, %s): %s', key, expectation, res.nil?))
852
557
  res
853
558
  end
854
559
 
855
- ##
856
- # generic_comparator
857
- #
858
- # powers the 3 argument form of constraint (i.e. 'is_virtual != true', '<package_version> > 3.0', etc)
859
- #
860
- # should really be an eval{} of some sort (or would be in the perl world)
861
- #
862
- # parameters
863
- # * <comparand1> - left side of the comparison
864
- # * <comparator> - comparison to make
865
- # * <comparand2> - right side of the comparison
866
560
  def generic_comparator(comparand1, comparator, comparand2)
867
561
 
868
562
  # TODO rewrite this as an eval so we don't have to support everything..
869
- # TODO come up with mechanism to determine when is it appropriate to call .to_i vs. otherwise -- comparisons will mainly be numerical (?), but need to support text matching too
870
563
  case comparator
871
564
  when '!='
872
565
  # ugh