chef-attribute-validator 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +20 -0
- data/CHANGES +4 -0
- data/Gemfile +2 -0
- data/LICENSE +27 -0
- data/README.md +109 -0
- data/Rakefile +27 -0
- data/chef-attribute-validator.gemspec +25 -0
- data/lib/chef-attribute-validator.rb +52 -0
- data/lib/chef-attribute-validator/attribute_set.rb +90 -0
- data/lib/chef-attribute-validator/check.rb +56 -0
- data/lib/chef-attribute-validator/checks/looks_like.rb +52 -0
- data/lib/chef-attribute-validator/checks/max_children.rb +37 -0
- data/lib/chef-attribute-validator/checks/min_children.rb +37 -0
- data/lib/chef-attribute-validator/checks/regex.rb +31 -0
- data/lib/chef-attribute-validator/checks/required.rb +42 -0
- data/lib/chef-attribute-validator/checks/type.rb +48 -0
- data/lib/chef-attribute-validator/rule.rb +54 -0
- data/lib/chef-attribute-validator/version.rb +7 -0
- data/lib/chef-attribute-validator/violation.rb +17 -0
- data/test/fixtures/attr_set.rb +10 -0
- data/test/fixtures/check_child_count.rb +104 -0
- data/test/fixtures/check_looks_like_arg_ip.rb +6 -0
- data/test/fixtures/check_looks_like_arg_regex.rb +6 -0
- data/test/fixtures/check_looks_like_arg_url.rb +6 -0
- data/test/fixtures/check_looks_like_arg_your_mom.rb +6 -0
- data/test/fixtures/check_looks_like_ip.rb +57 -0
- data/test/fixtures/check_looks_like_url.rb +39 -0
- data/test/fixtures/check_regex_assorted.rb +42 -0
- data/test/fixtures/check_regex_literal.rb +5 -0
- data/test/fixtures/check_regex_nil.rb +5 -0
- data/test/fixtures/check_regex_object.rb +5 -0
- data/test/fixtures/check_regex_string.rb +5 -0
- data/test/fixtures/check_required_assorted.rb +82 -0
- data/test/fixtures/check_required_false.rb +5 -0
- data/test/fixtures/check_required_true.rb +5 -0
- data/test/fixtures/check_required_zero.rb +5 -0
- data/test/fixtures/check_type.rb +270 -0
- data/test/fixtures/rules_empty.rb +2 -0
- data/test/fixtures/rules_missing_path.rb +1 -0
- data/test/fixtures/rules_no_check.rb +1 -0
- data/test/fixtures/rules_type_and_min_children.rb +3 -0
- data/test/fixtures/rules_type_check.rb +2 -0
- data/test/unit/attr_set_spec.rb +55 -0
- data/test/unit/check_child_count_spec.rb +109 -0
- data/test/unit/check_looks_like_spec.rb +106 -0
- data/test/unit/check_regex_spec.rb +62 -0
- data/test/unit/check_required_spec.rb +106 -0
- data/test/unit/check_type_spec.rb +191 -0
- data/test/unit/rule_parse_spec.rb +93 -0
- data/test/unit/spec_helper.rb +18 -0
- 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,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,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
|
+
}
|