puppet-lint 0.1.9 → 0.1.10
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.
- data/Rakefile +19 -0
- data/bin/puppet-lint +33 -11
- data/lib/puppet-lint.rb +112 -33
- data/lib/puppet-lint/configuration.rb +57 -0
- data/lib/puppet-lint/plugin.rb +122 -11
- data/lib/puppet-lint/plugins/check_classes.rb +57 -68
- data/lib/puppet-lint/plugins/check_conditionals.rb +19 -22
- data/lib/puppet-lint/plugins/check_resources.rb +42 -32
- data/lib/puppet-lint/plugins/check_strings.rb +34 -18
- data/lib/puppet-lint/plugins/check_variables.rb +11 -6
- data/lib/puppet-lint/plugins/check_whitespace.rb +39 -11
- data/puppet-lint.gemspec +3 -1
- data/spec/puppet-lint/check_classes_spec.rb +59 -53
- data/spec/puppet-lint/check_conditionals_spec.rb +5 -9
- data/spec/puppet-lint/check_resources_spec.rb +36 -35
- data/spec/puppet-lint/check_strings_spec.rb +17 -27
- data/spec/puppet-lint/check_variables_spec.rb +8 -3
- data/spec/puppet-lint/check_whitespace_spec.rb +7 -13
- data/spec/spec_helper.rb +98 -0
- metadata +19 -4
@@ -1,50 +1,47 @@
|
|
1
1
|
class PuppetLint::Plugins::CheckClasses < PuppetLint::CheckPlugin
|
2
|
-
|
3
|
-
lexer = Puppet::Parser::Lexer.new
|
4
|
-
lexer.string = data
|
5
|
-
tokens = lexer.fullscan
|
6
|
-
|
2
|
+
check 'right_to_left_relationship' do
|
7
3
|
tokens.select { |r| r.first == :OUT_EDGE }.each do |token|
|
8
|
-
|
4
|
+
notify :warning, :message => "right-to-left (<-) relationship", :linenumber => token.last[:line]
|
9
5
|
end
|
6
|
+
end
|
10
7
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
if [:CLASSNAME, :NAME].include? title_token.first
|
22
|
-
split_title = title_token.last[:value].split('::')
|
23
|
-
if split_title.length > 1
|
24
|
-
expected_path = "#{split_title.first}/manifests/#{split_title[1..-1].join('/')}.pp"
|
25
|
-
else
|
26
|
-
expected_path = "#{title_token.last[:value]}/manifests/init.pp"
|
27
|
-
end
|
8
|
+
check 'autoloader_layout' do
|
9
|
+
unless path == ""
|
10
|
+
(class_indexes + defined_type_indexes).each do |class_idx|
|
11
|
+
title_token = tokens[class_idx[:start]+1]
|
12
|
+
split_title = title_token.last[:value].split('::')
|
13
|
+
if split_title.length > 1
|
14
|
+
expected_path = "#{split_title.first}/manifests/#{split_title[1..-1].join('/')}.pp"
|
15
|
+
else
|
16
|
+
expected_path = "#{title_token.last[:value]}/manifests/init.pp"
|
17
|
+
end
|
28
18
|
|
29
|
-
|
30
|
-
|
31
|
-
end
|
32
|
-
end
|
19
|
+
unless path.end_with? expected_path
|
20
|
+
notify :error, :message => "#{title_token.last[:value]} not in autoload module layout", :linenumber => title_token.last[:line]
|
33
21
|
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
check 'parameter_order' do
|
27
|
+
(class_indexes + defined_type_indexes).each do |class_idx|
|
28
|
+
token_idx = class_idx[:start]
|
29
|
+
header_end_idx = tokens[token_idx..-1].index { |r| r.first == :LBRACE }
|
30
|
+
lparen_idx = tokens[token_idx..(header_end_idx + token_idx)].index { |r| r.first == :LPAREN }
|
31
|
+
rparen_idx = tokens[token_idx..(header_end_idx + token_idx)].rindex { |r| r.first == :RPAREN }
|
34
32
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
end
|
33
|
+
unless lparen_idx.nil? or rparen_idx.nil?
|
34
|
+
param_tokens = tokens[lparen_idx..rparen_idx]
|
35
|
+
param_tokens.each_index do |param_tokens_idx|
|
36
|
+
this_token = param_tokens[param_tokens_idx]
|
37
|
+
next_token = param_tokens[param_tokens_idx+1]
|
38
|
+
prev_token = param_tokens[param_tokens_idx-1]
|
39
|
+
if this_token.first == :VARIABLE
|
40
|
+
unless next_token.nil?
|
41
|
+
if next_token.first == :COMMA or next_token.first == :RPAREN
|
42
|
+
unless param_tokens[0..param_tokens_idx].rindex { |r| r.first == :EQUALS }.nil?
|
43
|
+
unless prev_token.nil? or prev_token.first == :EQUALS
|
44
|
+
notify :warning, :message => "optional parameter listed before required parameter", :linenumber => this_token.last[:line]
|
48
45
|
end
|
49
46
|
end
|
50
47
|
end
|
@@ -52,36 +49,24 @@ class PuppetLint::Plugins::CheckClasses < PuppetLint::CheckPlugin
|
|
52
49
|
end
|
53
50
|
end
|
54
51
|
end
|
52
|
+
end
|
53
|
+
end
|
55
54
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
unless class_name =~ /^#{inherited_class}::/
|
63
|
-
warn "class inherits across namespaces on line #{tokens[token_idx].last[:line]}"
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
55
|
+
check 'inherits_across_namespaces' do
|
56
|
+
class_indexes.each do |class_idx|
|
57
|
+
token_idx = class_idx[:start]
|
58
|
+
if tokens[token_idx+2].first == :INHERITS
|
59
|
+
class_name = tokens[token_idx+1].last[:value]
|
60
|
+
inherited_class = tokens[token_idx+3].last[:value]
|
67
61
|
|
68
|
-
|
69
|
-
|
70
|
-
idx = class_token_idx + token_idx
|
71
|
-
if tokens[idx].first == :LBRACE
|
72
|
-
lbrace_count += 1
|
73
|
-
elsif tokens[idx].first == :RBRACE
|
74
|
-
lbrace_count -= 1
|
75
|
-
if lbrace_count == 0
|
76
|
-
class_indexes << {:start => token_idx, :end => idx} if tokens[token_idx].first == :CLASS
|
77
|
-
defined_type_indexes << {:start => token_idx, :end => idx} if tokens[token_idx].first == :DEFINE
|
78
|
-
break
|
79
|
-
end
|
80
|
-
end
|
62
|
+
unless class_name =~ /^#{inherited_class}::/
|
63
|
+
notify :warning, :message => "class inherits across namespaces", :linenumber => tokens[token_idx].last[:line]
|
81
64
|
end
|
82
65
|
end
|
83
66
|
end
|
67
|
+
end
|
84
68
|
|
69
|
+
check 'nested_classes_or_defines' do
|
85
70
|
class_indexes.each do |class_idx|
|
86
71
|
class_tokens = tokens[class_idx[:start]..class_idx[:end]]
|
87
72
|
class_tokens[1..-1].each_index do |token_idx|
|
@@ -90,19 +75,21 @@ class PuppetLint::Plugins::CheckClasses < PuppetLint::CheckPlugin
|
|
90
75
|
|
91
76
|
if token.first == :CLASS
|
92
77
|
if next_token.first != :LBRACE
|
93
|
-
|
78
|
+
notify :warning, :message => "class defined inside a class", :linenumber => token.last[:line]
|
94
79
|
end
|
95
80
|
end
|
96
81
|
|
97
82
|
if token.first == :DEFINE
|
98
|
-
|
83
|
+
notify :warning, :message => "define defined inside a class", :linenumber => token.last[:line]
|
99
84
|
end
|
100
85
|
end
|
101
86
|
end
|
87
|
+
end
|
102
88
|
|
89
|
+
check 'variable_scope' do
|
103
90
|
(class_indexes + defined_type_indexes).each do |idx|
|
104
91
|
object_tokens = tokens[idx[:start]..idx[:end]]
|
105
|
-
variables_in_scope = ['name']
|
92
|
+
variables_in_scope = ['name', 'title', 'module_name']
|
106
93
|
referenced_variables = []
|
107
94
|
header_end_idx = object_tokens.index { |r| r.first == :LBRACE }
|
108
95
|
lparen_idx = object_tokens[0..header_end_idx].index { |r| r.first == :LPAREN }
|
@@ -137,7 +124,9 @@ class PuppetLint::Plugins::CheckClasses < PuppetLint::CheckPlugin
|
|
137
124
|
referenced_variables.each do |token|
|
138
125
|
unless token.last[:value].include? '::'
|
139
126
|
unless variables_in_scope.include? token.last[:value]
|
140
|
-
|
127
|
+
unless token.last[:value] =~ /\d+/
|
128
|
+
notify :warning, :message => "top-scope variable being used without an explicit namespace", :linenumber => token.last[:line]
|
129
|
+
end
|
141
130
|
end
|
142
131
|
end
|
143
132
|
end
|
@@ -1,10 +1,23 @@
|
|
1
1
|
class PuppetLint::Plugins::CheckConditionals < PuppetLint::CheckPlugin
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
2
|
+
check 'selector_inside_resource' do
|
3
|
+
resource_indexes.each do |resource|
|
4
|
+
resource_tokens = tokens[resource[:start]..resource[:end]]
|
5
|
+
|
6
|
+
resource_tokens.each_index do |resource_token_idx|
|
7
|
+
if resource_tokens[resource_token_idx].first == :FARROW
|
8
|
+
if resource_tokens[resource_token_idx + 1].first == :VARIABLE
|
9
|
+
unless resource_tokens[resource_token_idx + 2].nil?
|
10
|
+
if resource_tokens[resource_token_idx + 2].first == :QMARK
|
11
|
+
notify :warning, :message => "selector inside resource block", :linenumber => resource_tokens[resource_token_idx].last[:line]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
6
19
|
|
7
|
-
|
20
|
+
check 'case_without_default' do
|
8
21
|
case_indexes = []
|
9
22
|
|
10
23
|
tokens.each_index do |token_idx|
|
@@ -32,27 +45,11 @@ class PuppetLint::Plugins::CheckConditionals < PuppetLint::CheckPlugin
|
|
32
45
|
end
|
33
46
|
end
|
34
47
|
|
35
|
-
resource_indexes.each do |resource|
|
36
|
-
resource_tokens = tokens[resource[:start]..resource[:end]]
|
37
|
-
|
38
|
-
resource_tokens.each_index do |resource_token_idx|
|
39
|
-
if resource_tokens[resource_token_idx].first == :FARROW
|
40
|
-
if resource_tokens[resource_token_idx + 1].first == :VARIABLE
|
41
|
-
unless resource_tokens[resource_token_idx + 2].nil?
|
42
|
-
if resource_tokens[resource_token_idx + 2].first == :QMARK
|
43
|
-
warn "selector inside resource block on line #{resource_tokens[resource_token_idx].last[:line]}"
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
48
|
case_indexes.each do |kase|
|
52
49
|
case_tokens = tokens[kase[:start]..kase[:end]]
|
53
50
|
|
54
51
|
unless case_tokens.index { |r| r.first == :DEFAULT }
|
55
|
-
|
52
|
+
notify :warning, :message => "case statement without a default case", :linenumber => case_tokens.first.last[:line]
|
56
53
|
end
|
57
54
|
end
|
58
55
|
end
|
@@ -2,48 +2,30 @@
|
|
2
2
|
# http://docs.puppetlabs.com/guides/style_guide.html#resources
|
3
3
|
|
4
4
|
class PuppetLint::Plugins::CheckResources < PuppetLint::CheckPlugin
|
5
|
-
|
6
|
-
lexer = Puppet::Parser::Lexer.new
|
7
|
-
lexer.string = data
|
8
|
-
tokens = lexer.fullscan
|
9
|
-
|
10
|
-
title_tokens = []
|
11
|
-
resource_indexes = []
|
12
|
-
tokens.each_index do |token_idx|
|
13
|
-
if tokens[token_idx].first == :COLON
|
14
|
-
# gather a list of tokens that are resource titles
|
15
|
-
if tokens[token_idx-1].first == :RBRACK
|
16
|
-
title_array_tokens = tokens[tokens.rindex { |r| r.first == :LBRACK }+1..token_idx-2]
|
17
|
-
title_tokens += title_array_tokens.select { |token| [:STRING, :NAME].include? token.first }
|
18
|
-
else
|
19
|
-
if tokens[token_idx + 1].first != :LBRACE
|
20
|
-
title_tokens << tokens[token_idx-1]
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
# gather a list of start and end indexes for resource attribute blocks
|
25
|
-
if tokens[token_idx+1].first != :LBRACE
|
26
|
-
resource_indexes << {:start => token_idx+1, :end => tokens[token_idx+1..-1].index { |r| [:SEMIC, :RBRACE].include? r.first }+token_idx}
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
5
|
+
check 'unquoted_resource_title' do
|
31
6
|
title_tokens.each do |token|
|
32
7
|
if token.first == :NAME
|
33
|
-
|
8
|
+
notify :warning, :message => "unquoted resource title", :linenumber => token.last[:line]
|
34
9
|
end
|
35
10
|
end
|
11
|
+
end
|
36
12
|
|
13
|
+
check 'ensure_first_param' do
|
37
14
|
resource_indexes.each do |resource|
|
38
15
|
resource_tokens = tokens[resource[:start]..resource[:end]]
|
39
16
|
ensure_attr_index = resource_tokens.index { |token| token.first == :NAME and token.last[:value] == 'ensure' }
|
40
17
|
unless ensure_attr_index.nil?
|
41
18
|
if ensure_attr_index > 1
|
42
19
|
ensure_attr_line_no = resource_tokens[ensure_attr_index].last[:line]
|
43
|
-
|
20
|
+
notify :warning, :message => "ensure found on line but it's not the first attribute", :linenumber => ensure_attr_line_no
|
44
21
|
end
|
45
22
|
end
|
23
|
+
end
|
24
|
+
end
|
46
25
|
|
26
|
+
check 'unquoted_file_mode' do
|
27
|
+
resource_indexes.each do |resource|
|
28
|
+
resource_tokens = tokens[resource[:start]..resource[:end]]
|
47
29
|
resource_type_token = tokens[tokens[0..resource[:start]].rindex { |r| r.first == :LBRACE } - 1]
|
48
30
|
if resource_type_token.last[:value] == "file"
|
49
31
|
resource_tokens.each_index do |resource_token_idx|
|
@@ -51,15 +33,43 @@ class PuppetLint::Plugins::CheckResources < PuppetLint::CheckPlugin
|
|
51
33
|
if attr_token.first == :NAME and attr_token.last[:value] == 'mode'
|
52
34
|
value_token = resource_tokens[resource_token_idx + 2]
|
53
35
|
if value_token.first == :NAME
|
54
|
-
|
36
|
+
notify :warning, :message => "unquoted file mode", :linenumber => value_token.last[:line]
|
55
37
|
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
check '4digit_file_mode' do
|
45
|
+
resource_indexes.each do |resource|
|
46
|
+
resource_tokens = tokens[resource[:start]..resource[:end]]
|
47
|
+
resource_type_token = tokens[tokens[0..resource[:start]].rindex { |r| r.first == :LBRACE } - 1]
|
48
|
+
if resource_type_token.last[:value] == "file"
|
49
|
+
resource_tokens.each_index do |resource_token_idx|
|
50
|
+
attr_token = resource_tokens[resource_token_idx]
|
51
|
+
if attr_token.first == :NAME and attr_token.last[:value] == 'mode'
|
52
|
+
value_token = resource_tokens[resource_token_idx + 2]
|
56
53
|
if value_token.last[:value] !~ /\d{4}/ and value_token.first != :VARIABLE
|
57
|
-
|
54
|
+
notify :warning, :message => "mode should be represented as a 4 digit octal value", :linenumber => value_token.last[:line]
|
58
55
|
end
|
59
|
-
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
check 'ensure_not_symlink_target' do
|
63
|
+
resource_indexes.each do |resource|
|
64
|
+
resource_tokens = tokens[resource[:start]..resource[:end]]
|
65
|
+
resource_type_token = tokens[tokens[0..resource[:start]].rindex { |r| r.first == :LBRACE } - 1]
|
66
|
+
if resource_type_token.last[:value] == "file"
|
67
|
+
resource_tokens.each_index do |resource_token_idx|
|
68
|
+
attr_token = resource_tokens[resource_token_idx]
|
69
|
+
if attr_token.first == :NAME and attr_token.last[:value] == 'ensure'
|
60
70
|
value_token = resource_tokens[resource_token_idx + 2]
|
61
71
|
if value_token.last[:value].start_with? '/'
|
62
|
-
|
72
|
+
notify :warning, :message => "symlink target specified in ensure attr", :linenumber => value_token.last[:line]
|
63
73
|
end
|
64
74
|
end
|
65
75
|
end
|
@@ -14,27 +14,35 @@ class PuppetLint::Plugins::CheckStrings < PuppetLint::CheckPlugin
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
-
|
18
|
-
l = Puppet::Parser::Lexer.new
|
19
|
-
l.string = data
|
20
|
-
tokens = l.fullscan
|
21
|
-
|
17
|
+
check 'double_quoted_strings' do
|
22
18
|
tokens.each_index do |token_idx|
|
23
19
|
token = tokens[token_idx]
|
24
20
|
|
25
21
|
if token.first == :STRING
|
26
22
|
unless token.last[:value].include? "\t" or token.last[:value].include? "\n"
|
27
|
-
|
23
|
+
notify :warning, :message => "double quoted string containing no variables", :linenumber => token.last[:line]
|
28
24
|
end
|
29
25
|
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
check 'only_variable_string' do
|
30
|
+
tokens.each_index do |token_idx|
|
31
|
+
token = tokens[token_idx]
|
30
32
|
|
31
33
|
if token.first == :DQPRE and token.last[:value] == ""
|
32
34
|
if tokens[token_idx + 1].first == :VARIABLE
|
33
35
|
if tokens[token_idx + 2].first == :DQPOST and tokens[token_idx + 2].last[:value] == ""
|
34
|
-
|
36
|
+
notify :warning, :message => "string containing only a variable", :linenumber => tokens[token_idx + 1].last[:line]
|
35
37
|
end
|
36
38
|
end
|
37
39
|
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
check 'variables_not_enclosed' do
|
44
|
+
tokens.each_index do |token_idx|
|
45
|
+
token = tokens[token_idx]
|
38
46
|
|
39
47
|
if token.first == :DQPRE
|
40
48
|
end_of_string_idx = tokens[token_idx..-1].index { |r| r.first == :DQPOST }
|
@@ -42,31 +50,39 @@ class PuppetLint::Plugins::CheckStrings < PuppetLint::CheckPlugin
|
|
42
50
|
if t.first == :VARIABLE
|
43
51
|
line = data.split("\n")[t.last[:line] - 1]
|
44
52
|
if line.is_a? String and line.include? "$#{t.last[:value]}"
|
45
|
-
|
53
|
+
notify :warning, :message => "variable not enclosed in {}", :linenumber => t.last[:line]
|
46
54
|
end
|
47
55
|
end
|
48
56
|
end
|
49
57
|
end
|
58
|
+
end
|
59
|
+
end
|
50
60
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
if t.first == :VARIABLE and t.last[:value].match(/-/)
|
55
|
-
warn "variable contains a dash on line #{t.last[:line]}"
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
61
|
+
check 'single_quote_string_with_variables' do
|
62
|
+
tokens.each_index do |token_idx|
|
63
|
+
token = tokens[token_idx]
|
59
64
|
|
60
65
|
if token.first == :SSTRING
|
61
66
|
contents = token.last[:value]
|
62
67
|
line_no = token.last[:line]
|
63
68
|
|
64
69
|
if contents.include? '${'
|
65
|
-
error "single quoted string containing a variable found
|
70
|
+
notify :error, :message => "single quoted string containing a variable found", :linenumber => token.last[:line]
|
66
71
|
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
check 'quoted_booleans' do
|
77
|
+
tokens.each_index do |token_idx|
|
78
|
+
token = tokens[token_idx]
|
79
|
+
|
80
|
+
if token.first == :SSTRING
|
81
|
+
contents = token.last[:value]
|
82
|
+
line_no = token.last[:line]
|
67
83
|
|
68
84
|
if ['true', 'false'].include? contents
|
69
|
-
|
85
|
+
notify :warning, :message => "quoted boolean value found", :linenumber => token.last[:line]
|
70
86
|
end
|
71
87
|
end
|
72
88
|
end
|
@@ -1,9 +1,5 @@
|
|
1
1
|
class PuppetLint::Plugins::CheckVariables < PuppetLint::CheckPlugin
|
2
|
-
|
3
|
-
lexer = Puppet::Parser::Lexer.new
|
4
|
-
lexer.string = data
|
5
|
-
tokens = lexer.fullscan
|
6
|
-
|
2
|
+
check 'variable_contains_dash' do
|
7
3
|
tokens.each_index do |token_idx|
|
8
4
|
token = tokens[token_idx]
|
9
5
|
|
@@ -11,7 +7,16 @@ class PuppetLint::Plugins::CheckVariables < PuppetLint::CheckPlugin
|
|
11
7
|
variable = token.last[:value]
|
12
8
|
line_no = token.last[:line]
|
13
9
|
if variable.match(/-/)
|
14
|
-
|
10
|
+
notify :warning, :message => "variable contains a dash", :linenumber => line_no
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
if token.first == :DQPRE
|
15
|
+
end_of_string_idx = tokens[token_idx..-1].index { |r| r.first == :DQPOST }
|
16
|
+
tokens[token_idx..end_of_string_idx].each do |t|
|
17
|
+
if t.first == :VARIABLE and t.last[:value].match(/-/)
|
18
|
+
notify :warning, :message => "variable contains a dash", :linenumber => t.last[:line]
|
19
|
+
end
|
15
20
|
end
|
16
21
|
end
|
17
22
|
end
|