chef-attribute-validator 0.1.0

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