puppet-lint 0.1.9 → 0.1.10
Sign up to get free protection for your applications and to get access to all the features.
- 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
|