Skip to content

Commit 4d96c8e

Browse files
Merge pull request #85 from puppetlabs/CONT-339-add_top_scope_facts_check
(CONT-339) Add top scope facts check
2 parents dbc093c + d416062 commit 4d96c8e

File tree

2 files changed

+232
-0
lines changed

2 files changed

+232
-0
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Public: A puppet-lint plugin that will check for the use of top scope facts.
2+
# For example, the fact `$facts['kernel']` should be used over
3+
# `$::kernel`.
4+
#
5+
# The check only finds facts using the top-scope: ie it will find $::operatingsystem
6+
# but not $operatingsystem. It also all top scope variables are facts.
7+
# If you have top scope variables that aren't facts you should configure the
8+
# linter to ignore them.
9+
#
10+
# You can whitelist top scope variables to ignore via the Rake task.
11+
# You should insert the following line to your Rakefile.
12+
# `PuppetLint.configuration.top_scope_variables = ['location', 'role']`
13+
#
14+
# This plugin was adoped in to puppet-lint from https://github.com/mmckinst/puppet-lint-top_scope_facts-check
15+
# Thanks to @mmckinst and @seanmil for the original work.
16+
PuppetLint.new_check(:top_scope_facts) do
17+
TOP_SCOPE_FACTS_VAR_TYPES = Set[:VARIABLE, :UNENC_VARIABLE]
18+
def check
19+
whitelist = ['trusted', 'facts'] + (PuppetLint.configuration.top_scope_variables || [])
20+
whitelist = whitelist.join('|')
21+
tokens.select { |x| TOP_SCOPE_FACTS_VAR_TYPES.include?(x.type) }.each do |token|
22+
next unless %r{^::}.match?(token.value)
23+
next if %r{^::(#{whitelist})\[?}.match?(token.value)
24+
next if %r{^::[a-z0-9_][a-zA-Z0-9_]+::}.match?(token.value)
25+
26+
notify :warning, {
27+
message: 'top scope fact instead of facts hash',
28+
line: token.line,
29+
column: token.column,
30+
token: token,
31+
}
32+
end
33+
end
34+
35+
def fix(problem)
36+
problem[:token].value = "facts['" + problem[:token].value.sub(%r{^::}, '') + "']"
37+
end
38+
end
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
require 'spec_helper'
2+
3+
describe 'top_scope_facts' do
4+
let(:msg) { 'top scope fact instead of facts hash' }
5+
6+
context 'with fix disabled' do
7+
context 'fact variable using $facts hash' do
8+
let(:code) { "$facts['operatingsystem']" }
9+
10+
it 'does not detect any problems' do
11+
expect(problems).to have(0).problem
12+
end
13+
end
14+
context 'non-fact variable with two colons' do
15+
let(:code) { '$foo::bar' }
16+
17+
it 'does not detect any problems' do
18+
expect(problems).to have(0).problem
19+
end
20+
end
21+
22+
context 'top scope $::facts hash' do
23+
let(:code) { "$::facts['os']['family']" }
24+
25+
it 'does not detect any problems' do
26+
expect(problems).to have(0).problem
27+
end
28+
end
29+
30+
context 'top scope $::trusted hash' do
31+
let(:code) { "$::trusted['certname']" }
32+
33+
it 'does not detect any problems' do
34+
expect(problems).to have(0).problem
35+
end
36+
end
37+
38+
context 'fact variable using top scope' do
39+
let(:code) { '$::operatingsystem' }
40+
41+
it 'onlies detect a single problem' do
42+
expect(problems).to have(1).problem
43+
end
44+
45+
it 'creates a warning' do
46+
expect(problems).to contain_warning(msg).on_line(1).in_column(1)
47+
end
48+
end
49+
50+
context 'fact variable using top scope with curly braces in double quote' do
51+
let(:code) { '"${::operatingsystem}"' }
52+
53+
it 'onlies detect a single problem' do
54+
expect(problems).to have(1).problem
55+
end
56+
57+
it 'creates a warning' do
58+
expect(problems).to contain_warning(msg).on_line(1).in_column(4)
59+
end
60+
end
61+
62+
context 'out of scope namespaced variable with leading ::' do
63+
let(:code) { '$::profile::foo::bar' }
64+
65+
it 'does not detect any problems' do
66+
expect(problems).to have(0).problem
67+
end
68+
69+
context 'inside double quotes' do
70+
let(:code) { '"$::profile::foo::bar"' }
71+
72+
it 'does not detect any problems' do
73+
expect(problems).to have(0).problem
74+
end
75+
end
76+
77+
context 'with curly braces in double quote' do
78+
let(:code) { '"${::profile::foo::bar}"' }
79+
80+
it 'does not detect any problems' do
81+
expect(problems).to have(0).problem
82+
end
83+
end
84+
end
85+
end
86+
87+
context 'with fix enabled' do
88+
before(:each) do
89+
PuppetLint.configuration.fix = true
90+
end
91+
92+
after(:each) do
93+
PuppetLint.configuration.fix = false
94+
end
95+
96+
context 'fact variable using $facts hash' do
97+
let(:code) { "$facts['operatingsystem']" }
98+
99+
it 'does not detect any problems' do
100+
expect(problems).to have(0).problem
101+
end
102+
end
103+
104+
context 'non-fact variable with two colons' do
105+
let(:code) { '$foo::bar' }
106+
107+
it 'does not detect any problems' do
108+
expect(problems).to have(0).problem
109+
end
110+
end
111+
112+
context 'top scope $::facts hash' do
113+
let(:code) { "$::facts['os']['family']" }
114+
115+
it 'does not detect any problems' do
116+
expect(problems).to have(0).problem
117+
end
118+
end
119+
120+
context 'top scope $::trusted hash' do
121+
let(:code) { "$::trusted['certname']" }
122+
123+
it 'does not detect any problems' do
124+
expect(problems).to have(0).problem
125+
end
126+
end
127+
128+
context 'fact variable using top scope' do
129+
let(:code) { '$::operatingsystem' }
130+
131+
it 'onlies detect a single problem' do
132+
expect(problems).to have(1).problem
133+
end
134+
135+
it 'fixes the problem' do
136+
expect(problems).to contain_fixed(msg).on_line(1).in_column(1)
137+
end
138+
139+
it 'shoulds use the facts hash' do
140+
expect(manifest).to eq("$facts['operatingsystem']")
141+
end
142+
end
143+
144+
context 'fact variable using top scope with curly braces in double quote' do
145+
let(:code) { '"${::operatingsystem}"' }
146+
147+
it 'fixes the problem' do
148+
expect(problems).to contain_fixed(msg).on_line(1).in_column(4)
149+
end
150+
151+
it 'shoulds use the facts hash' do
152+
expect(manifest).to eq('"${facts[\'operatingsystem\']}"')
153+
end
154+
end
155+
156+
context 'with custom top scope fact variables' do
157+
before(:each) do
158+
PuppetLint.configuration.top_scope_variables = ['location', 'role']
159+
end
160+
161+
context 'fact variable using $facts hash' do
162+
let(:code) { "$facts['operatingsystem']" }
163+
164+
it 'does not detect any problems' do
165+
expect(problems).to have(0).problem
166+
end
167+
end
168+
169+
context 'fact variable using $trusted hash' do
170+
let(:code) { "$trusted['certname']" }
171+
172+
it 'does not detect any problems' do
173+
expect(problems).to have(0).problem
174+
end
175+
end
176+
177+
context 'whitelisted top scope variable $::location' do
178+
let(:code) { '$::location' }
179+
180+
it 'does not detect any problems' do
181+
expect(problems).to have(0).problem
182+
end
183+
end
184+
185+
context 'non-whitelisted top scope variable $::application' do
186+
let(:code) { '$::application' }
187+
188+
it 'does not detect any problems' do
189+
expect(problems).to have(1).problem
190+
end
191+
end
192+
end
193+
end
194+
end

0 commit comments

Comments
 (0)