chef-attribute-validator 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +20 -0
  3. data/CHANGES +4 -0
  4. data/Gemfile +2 -0
  5. data/LICENSE +27 -0
  6. data/README.md +109 -0
  7. data/Rakefile +27 -0
  8. data/chef-attribute-validator.gemspec +25 -0
  9. data/lib/chef-attribute-validator.rb +52 -0
  10. data/lib/chef-attribute-validator/attribute_set.rb +90 -0
  11. data/lib/chef-attribute-validator/check.rb +56 -0
  12. data/lib/chef-attribute-validator/checks/looks_like.rb +52 -0
  13. data/lib/chef-attribute-validator/checks/max_children.rb +37 -0
  14. data/lib/chef-attribute-validator/checks/min_children.rb +37 -0
  15. data/lib/chef-attribute-validator/checks/regex.rb +31 -0
  16. data/lib/chef-attribute-validator/checks/required.rb +42 -0
  17. data/lib/chef-attribute-validator/checks/type.rb +48 -0
  18. data/lib/chef-attribute-validator/rule.rb +54 -0
  19. data/lib/chef-attribute-validator/version.rb +7 -0
  20. data/lib/chef-attribute-validator/violation.rb +17 -0
  21. data/test/fixtures/attr_set.rb +10 -0
  22. data/test/fixtures/check_child_count.rb +104 -0
  23. data/test/fixtures/check_looks_like_arg_ip.rb +6 -0
  24. data/test/fixtures/check_looks_like_arg_regex.rb +6 -0
  25. data/test/fixtures/check_looks_like_arg_url.rb +6 -0
  26. data/test/fixtures/check_looks_like_arg_your_mom.rb +6 -0
  27. data/test/fixtures/check_looks_like_ip.rb +57 -0
  28. data/test/fixtures/check_looks_like_url.rb +39 -0
  29. data/test/fixtures/check_regex_assorted.rb +42 -0
  30. data/test/fixtures/check_regex_literal.rb +5 -0
  31. data/test/fixtures/check_regex_nil.rb +5 -0
  32. data/test/fixtures/check_regex_object.rb +5 -0
  33. data/test/fixtures/check_regex_string.rb +5 -0
  34. data/test/fixtures/check_required_assorted.rb +82 -0
  35. data/test/fixtures/check_required_false.rb +5 -0
  36. data/test/fixtures/check_required_true.rb +5 -0
  37. data/test/fixtures/check_required_zero.rb +5 -0
  38. data/test/fixtures/check_type.rb +270 -0
  39. data/test/fixtures/rules_empty.rb +2 -0
  40. data/test/fixtures/rules_missing_path.rb +1 -0
  41. data/test/fixtures/rules_no_check.rb +1 -0
  42. data/test/fixtures/rules_type_and_min_children.rb +3 -0
  43. data/test/fixtures/rules_type_check.rb +2 -0
  44. data/test/unit/attr_set_spec.rb +55 -0
  45. data/test/unit/check_child_count_spec.rb +109 -0
  46. data/test/unit/check_looks_like_spec.rb +106 -0
  47. data/test/unit/check_regex_spec.rb +62 -0
  48. data/test/unit/check_required_spec.rb +106 -0
  49. data/test/unit/check_type_spec.rb +191 -0
  50. data/test/unit/rule_parse_spec.rb +93 -0
  51. data/test/unit/spec_helper.rb +18 -0
  52. metadata +170 -0
@@ -0,0 +1,37 @@
1
+ class Chef
2
+ class Attribute
3
+ class Validator
4
+ class Check
5
+ class MaxChildren < Check
6
+
7
+ register_check('max_children', MaxChildren)
8
+
9
+ def validate_check_arg
10
+ unless check_arg.kind_of?(::Fixnum)
11
+ raise "Bad 'max_children' check argument '#{check_arg}' for rule '#{rule_name}' - expected an integer."
12
+ end
13
+ end
14
+
15
+ def check(attrset)
16
+ violations = []
17
+
18
+ # So if you say there must be at most one child, and the attributes don't even exist, is that a violation?
19
+ # I say no; if you care if the attributes exist, use required.
20
+ attrset.each do |path, value|
21
+ if value.kind_of?(Array) || value.kind_of?(Mash)
22
+ if value.size > check_arg
23
+ violations.push Chef::Attribute::Validator::Violation.new(rule_name, path, "Attribute should have at most #{check_arg} child values, but has #{value.size}")
24
+ end
25
+ else
26
+ violations.push Chef::Attribute::Validator::Violation.new(rule_name, path, "Attribute should have at most #{check_arg} child values, but is of the wrong type - should be hash or array.")
27
+ end
28
+ end
29
+ violations
30
+
31
+ end
32
+
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,37 @@
1
+ class Chef
2
+ class Attribute
3
+ class Validator
4
+ class Check
5
+ class MinChildren < Check
6
+
7
+ register_check('min_children', MinChildren)
8
+
9
+ def validate_check_arg
10
+ unless check_arg.kind_of?(::Fixnum)
11
+ raise "Bad 'min_children' check argument '#{check_arg}' for rule '#{rule_name}' - expected an integer."
12
+ end
13
+ end
14
+
15
+ def check(attrset)
16
+ violations = []
17
+
18
+ # So if you say there must be at least one child, and the attributes don't even exist, is that a violation?
19
+ # I say no; if you can if the attributes exist, use required.
20
+ attrset.each do |path, value|
21
+ if value.kind_of?(Array) || value.kind_of?(Mash)
22
+ if value.size < check_arg
23
+ violations.push Chef::Attribute::Validator::Violation.new(rule_name, path, "Attribute should have at least #{check_arg} child values, but has only #{value.size}")
24
+ end
25
+ else
26
+ violations.push Chef::Attribute::Validator::Violation.new(rule_name, path, "Attribute should have at least #{check_arg} child values, but is of the wrong type - should be hash or array.")
27
+ end
28
+ end
29
+ violations
30
+
31
+ end
32
+
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,31 @@
1
+ class Chef
2
+ class Attribute
3
+ class Validator
4
+ class Check
5
+ class Regex < Check
6
+
7
+ register_check('regex', Regex)
8
+
9
+ def validate_check_arg
10
+ unless check_arg.kind_of?(Regexp)
11
+ raise "Bad 'regex' check argument '#{check_arg}' for rule '#{rule_name}' - expected a Regexp"
12
+ end
13
+ end
14
+
15
+ def check(attrset)
16
+ violations = []
17
+ attrset.each do |path, value|
18
+ if val_scalar?(value)
19
+ unless check_arg.match(value)
20
+ violations.push Chef::Attribute::Validator::Violation.new(rule_name, path, "Attribute's value is '#{value}', which does not match regex '#{check_arg}'")
21
+ end
22
+ end
23
+ end
24
+ violations
25
+ end
26
+
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,42 @@
1
+ class Chef
2
+ class Attribute
3
+ class Validator
4
+ class Check
5
+ class Required < Check
6
+
7
+ register_check('required', Required)
8
+
9
+ def validate_check_arg
10
+ unless check_arg.kind_of?(TrueClass) || check_arg.kind_of?(FalseClass)
11
+ raise "Bad 'required' check argument '#{check_arg}' for rule '#{rule_name}' - expected one of true,false"
12
+ end
13
+ end
14
+
15
+ def check(attrset)
16
+ violations = []
17
+ if check_arg # If we are not required, we're effectively a no-op
18
+ if attrset.empty?
19
+ violations.push Chef::Attribute::Validator::Violation.new(rule_name, path_spec, "No attributes exist for path '#{path_spec}', but this rule says it is required.")
20
+ else
21
+ attrset.each do |path, value|
22
+ if val_scalar?(value)
23
+ if value.nil?
24
+ violations.push Chef::Attribute::Validator::Violation.new(rule_name, path, "Attributes value is nil, but is required.")
25
+ elsif value == ""
26
+ violations.push Chef::Attribute::Validator::Violation.new(rule_name, path, "Attributes value is '', but is required.")
27
+ end
28
+ else
29
+ if value.size == 0
30
+ violations.push Chef::Attribute::Validator::Violation.new(rule_name, path, "Attribute has no children, but is required.")
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ violations
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,48 @@
1
+ class Chef
2
+ class Attribute
3
+ class Validator
4
+ class Check
5
+ class Type < Check
6
+
7
+ register_check('type', Type)
8
+
9
+ def validate_check_arg
10
+ expected = [
11
+ 'string',
12
+ 'number',
13
+ 'boolean',
14
+ 'hash',
15
+ 'array',
16
+ ]
17
+
18
+ unless expected.include?(check_arg)
19
+ raise "Bad 'type' check argument '#{checkarg}' for rule '#{rule_name}' - expected one of #{expected.join(',')}"
20
+ end
21
+ end
22
+
23
+ def check(attrset)
24
+ violations = []
25
+
26
+ klasses = {
27
+ 'string' => [ String ],
28
+ 'number' => [ Integer, Float ],
29
+ 'boolean' => [ TrueClass, FalseClass ],
30
+ 'array' => [ Array ],
31
+ 'hash' => [ Mash ],
32
+ 'nil' => [ NilClass ],
33
+ }
34
+
35
+ attrset.each do |path, value|
36
+ unless klasses[check_arg].any? {|k| value.kind_of?(k) }
37
+ violations.push Chef::Attribute::Validator::Violation.new(rule_name, path, "Attribute's value is '#{value}', which does not appear to be the right type - expected '#{check_arg}'")
38
+ end
39
+ end
40
+
41
+ violations
42
+ end
43
+
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,54 @@
1
+ require_relative './attribute_set.rb'
2
+ require_relative './check.rb'
3
+
4
+ class Chef
5
+ class Attribute
6
+ class Validator
7
+ class Rule
8
+ attr_accessor :name
9
+ attr_accessor :path
10
+ attr_accessor :checks
11
+ attr_accessor :enabled
12
+
13
+ def initialize (a_name, a_def)
14
+ @name = a_name
15
+ @path = a_def['path']
16
+
17
+ unless path
18
+ raise "Missing 'path' attribute for attribute validation rule '#{name}'"
19
+ end
20
+
21
+ @enabled = true
22
+ if a_def.has_key?('enabled')
23
+ @enabled = a_def['enabled']
24
+ end
25
+
26
+ @checks = {}
27
+ a_def.keys.reject { |ck| %w(path enabled).include? ck }.sort.each do |check_name|
28
+ checks[check_name] = Check.make(check_name, self, a_def[check_name])
29
+ end
30
+ end
31
+
32
+ def apply(node)
33
+ violations = []
34
+ if enabled
35
+ attrset = Chef::Attribute::Validator::AttributeSet.new(node, path)
36
+ checks.each do |check_name, check_obj|
37
+ violations += check_obj.check(attrset)
38
+ end
39
+ end
40
+ violations
41
+ end
42
+
43
+ def check_count
44
+ checks.keys.size
45
+ end
46
+
47
+ def has_check?(check_name)
48
+ checks.has_key?(check_name)
49
+ end
50
+
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,7 @@
1
+ class Chef
2
+ class Attribute
3
+ class Validator
4
+ VERSION = "0.1.0"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,17 @@
1
+ class Chef
2
+ class Attribute
3
+ class Validator
4
+ class Violation
5
+ attr_reader :rule_name
6
+ attr_reader :path
7
+ attr_reader :message
8
+
9
+ def initialize(a_rule_name, a_path, a_message)
10
+ @rule_name = a_rule_name
11
+ @path = a_path
12
+ @message = a_message
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,10 @@
1
+ default['one'] = 1
2
+ default['two'] = 2
3
+ default['deeper']['one'] = 1
4
+ default['deeper']['deeper_yet']['one'] = 1
5
+ default['cats'] = ['maine coon', 'tabby', 'siamese']
6
+ default['rats'] = ['Nicodemus', 'Ratatouille']
7
+ default['array_buildup'] = []
8
+ default['array_buildup'] = [ 'a' ]
9
+ default['array_buildup'] = [ 'b' ]
10
+
@@ -0,0 +1,104 @@
1
+ rules = default['attribute-validator']['rules']
2
+
3
+ default['nil'] = nil
4
+ default['string'] = "pants"
5
+
6
+ default['array']['empty'] = []
7
+ default['array']['one'] = [ 'mellow gold' ]
8
+ default['array']['two'] = [ 'mellow gold', 'odelay' ]
9
+ default['array']['fifty'] = ['a'] * 50
10
+
11
+ default['hash']['empty'] = {}
12
+ default['hash']['one'] = { 'a' => 1 }
13
+ default['hash']['two'] = { 'a' => 1, 'b' => 2 }
14
+
15
+ rules['min-nil'] = {
16
+ 'path' => '/nil',
17
+ 'min_children' => 1
18
+ }
19
+
20
+ rules['min-string'] = {
21
+ 'path' => '/string',
22
+ 'min_children' => 1
23
+ }
24
+
25
+ rules['min-array-empty-zero'] = {
26
+ 'path' => '/array/empty',
27
+ 'min_children' => 0
28
+ }
29
+
30
+ rules['min-array-empty-one'] = {
31
+ 'path' => '/array/empty',
32
+ 'min_children' => 1
33
+ }
34
+
35
+ rules['min-array-one-two'] = {
36
+ 'path' => '/array/one',
37
+ 'min_children' => 2
38
+ }
39
+
40
+ rules['min-hash-empty-zero'] = {
41
+ 'path' => '/hash/empty',
42
+ 'min_children' => 0
43
+ }
44
+
45
+ rules['min-hash-empty-one'] = {
46
+ 'path' => '/hash/empty',
47
+ 'min_children' => 1
48
+ }
49
+
50
+ rules['min-hash-one-two'] = {
51
+ 'path' => '/hash/one',
52
+ 'min_children' => 2
53
+ }
54
+
55
+ rules['max-nil'] = {
56
+ 'path' => '/nil',
57
+ 'max_children' => 1
58
+ }
59
+
60
+ rules['max-string'] = {
61
+ 'path' => '/string',
62
+ 'max_children' => 1
63
+ }
64
+
65
+ rules['max-array-empty-zero'] = {
66
+ 'path' => '/array/empty',
67
+ 'max_children' => 0
68
+ }
69
+
70
+ rules['max-array-zero-one'] = {
71
+ 'path' => '/array/one',
72
+ 'max_children' => 0
73
+ }
74
+
75
+ rules['max-array-one-two'] = {
76
+ 'path' => '/array/two',
77
+ 'max_children' => 1
78
+ }
79
+
80
+ rules['max-array-two-two'] = {
81
+ 'path' => '/array/two',
82
+ 'max_children' => 2
83
+ }
84
+
85
+ rules['max-hash-empty-zero'] = {
86
+ 'path' => '/hash/empty',
87
+ 'max_children' => 0
88
+ }
89
+
90
+ rules['max-hash-zero-one'] = {
91
+ 'path' => '/hash/one',
92
+ 'max_children' => 0
93
+ }
94
+
95
+ rules['max-hash-one-two'] = {
96
+ 'path' => '/hash/two',
97
+ 'max_children' => 1
98
+ }
99
+
100
+ rules['max-hash-two-two'] = {
101
+ 'path' => '/hash/two',
102
+ 'max_children' => 2
103
+ }
104
+
@@ -0,0 +1,6 @@
1
+ rules = default['attribute-validator']['rules']
2
+ rules['check-looks-like-arg-ip'] = {
3
+ 'path' => '/nil',
4
+ 'looks_like' => 'ip',
5
+ }
6
+
@@ -0,0 +1,6 @@
1
+ rules = default['attribute-validator']['rules']
2
+ rules['check-looks-like-arg-regex'] = {
3
+ 'path' => '/nil',
4
+ 'looks_like' => /foo/,
5
+ }
6
+
@@ -0,0 +1,6 @@
1
+ rules = default['attribute-validator']['rules']
2
+ rules['check-looks-like-arg-url'] = {
3
+ 'path' => '/nil',
4
+ 'looks_like' => 'url',
5
+ }
6
+
@@ -0,0 +1,6 @@
1
+ rules = default['attribute-validator']['rules']
2
+ rules['check-looks-like-arg-your-mom'] = {
3
+ 'path' => '/nil',
4
+ 'looks_like' => 'your mom',
5
+ }
6
+
@@ -0,0 +1,57 @@
1
+
2
+ default['nil'] = nil
3
+ default['malformed'] = '1234.123.123.123'
4
+ default['merryxmas'] = '255.255.255.255'
5
+ default['deranged'] = '300.300.300.1'
6
+ default['3not4'] = '123.123.123'
7
+ default['allones'] = '1.1.1.1'
8
+ default['slash24'] = '1.1.1.1/24'
9
+
10
+ default['v6']['empty'] = '::'
11
+
12
+ rules = default['attribute-validator']['rules']
13
+ rules['ip-nil'] = {
14
+ 'path' => '/nil',
15
+ 'looks_like' => 'ip',
16
+ }
17
+
18
+ rules['ip-missing'] = {
19
+ 'path' => '/nope',
20
+ 'looks_like' => 'ip',
21
+ }
22
+
23
+ rules['ip-malformed'] = {
24
+ 'path' => '/malformed',
25
+ 'looks_like' => 'ip',
26
+ }
27
+
28
+ rules['ip-merryxmas'] = {
29
+ 'path' => '/merryxmas',
30
+ 'looks_like' => 'ip',
31
+ }
32
+
33
+ rules['ip-deranged'] = {
34
+ 'path' => '/deranged',
35
+ 'looks_like' => 'ip',
36
+ }
37
+
38
+ rules['ip-3not4'] = {
39
+ 'path' => '/3not4',
40
+ 'looks_like' => 'ip',
41
+ }
42
+
43
+ rules['ip-allones'] = {
44
+ 'path' => '/allones',
45
+ 'looks_like' => 'ip',
46
+ }
47
+
48
+ rules['ip-slash24'] = {
49
+ 'path' => '/slash24',
50
+ 'looks_like' => 'ip',
51
+ }
52
+
53
+
54
+ rules['ip-emptyv6'] = {
55
+ 'path' => '/v6/empty',
56
+ 'looks_like' => 'ip',
57
+ }