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.
- 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
|
+
}
|